[Mono-list] Question about C#

Jonathan Pryor jonpryor@vt.edu
10 Sep 2002 11:50:07 -0400


Inline... 

On Mon, 2002-09-09 at 23:49, Ricardo Kirkner wrote: 

<snip/> 
            2. If I want to overload the ++ operator in a class that I
            dont want to be public, but internal or private, how can I
            prevent someone from accessing a method (the ++ operator)
            that has been defined public?
        Question to ponder: how will they access the public method if
        they can't access the class in the first place?  Consider
        methods that must be public, such as Object.ToString().  Is it a
        problem that this method is public, even in your internal
        classes?  Not usually -- non-internal users can't access the
        class, so the existence of a public ToString() method shouldn't
        be a problem.  The same should be true of any operators.
        
        (The answer to the above question of accessing internal classes
        is simple, actually: use Reflection and poke around... 
        Alternatively, modify the runtime to permit poking on internal
        data.  It's hard to protect against the runtime....  However,
        these ideas are not trivial, and can probably be ignored most of
        the time.)
    This is not really a clean solution, is it? I mean, although I
    cannot access the class (so I dont have to worry about the
    "public-nes" of the methods inside that class, it does not mean that
    it is correct to force you to declare the method public. (although
    this could be, and probably is, just a simplification made by the
    language designers)

Again, I'm not sure why operators need to be public.  However, the
reason for requiring that overridden methods not change the access
specifiers is to minimize confusion.  Through polymorphism, you're able
to invoke a derived class implementation through a reference to a base
class.  Wouldn't it be weird if you couldn't invoke the same method
through a reference to the derived class?  Consider the following
C#-like code:

    class Base {
        public virtual void Calculate () {/* ... */}
    }
    
    class Derived : Base {
        // Note: change protection; not allowed in C#
        protected override void Calculate () { /* ... */ }
    }
    
    class User {
        public static void Main () {
            Base b = new Derived ();
    
            // Legal; method is public
            b.Calculate ();
    
            Derived d = new Derived ();
    
            // Not legal; method is protected!
            d.Calculate ();
        }
    }
Requiring that overridden methods provide the same access specifiers as
the base method can be seen as a simplification of the language.  It can
also be seen as a sane requirement, reducing programmer confusion in
situations like the example above.

        However, the real question remains: why do you need an
        assignment operator?  Assignment operators are useful in C++
        when wrapping resources, such as memory, files, thread locks,
        etc., to make sure that the resource is properly managed (in
        concert with the copy constructor and destructor)...  In C#, the
        garbage collector is used for resource management, removing
        (what I've found) the greatest need for the trio of C++ copy
        constructor, assignment operator, and destructor.  If you need
        something more deterministic, the IDisposable interface/idiom is
        appropriate.
        
    I actually dont have a need for an assignment operator, but I think
    it is really nice if you can do something like
     
        obj1 = obj2;
     
    instead of
        
        obj1 = new ObjectType(obj2);
     
    This whole issue arises because c# treats objects as references to
    objects instead as the real ones (not that I find this wrong). I
    think that if you use a language that tries to be so simple as c#
    is, and that tries to "help" the programmer by simplifying syntax,
    etc, then it is suspicious not being able to write a simple object
    assignment  (without having both variables referencing the same
    object. Note that by simle I mean using the = operator). This
    could be just a decision made by the folks who thought about the
    language (and then I could not make anything about it, of course),
    so I dont want to make a big discussion about this here (because it
    is really a C# topic and not mono specific).

Simplicity is often achieved by removing features.  Java is often
considered to be simple because it removed multiple inheritance,
operator overloading, enumerations, templates, and lots of other fun C++
features.  In comparison, C# is considerably less simple than Java, but
still simpler than C++.  For example, C# followed the Simula-like (and
Java-like) bifurcation of the type system.  You have stack-allocated
types, and heap-allocated types, and never the two shall meet.  (This is
true for Simula and Java, but C# lets you convert the stack-allocated
objects into heap-allocated objects, bridging the type system.  You
still can't have any object-type be located on the stack, though.) 
Stroustrup hated this bifurcation, which is why C++ (by default) allows
objects to be allocated anywhere.  However, C++'s flexibility leads to
complexity.  The ability to store any object anywhere, coupled with the
lack of a garbage collector, requires that the programmer be much more
involved with the lifetime of objects than is required in other
programming languages.

So .NET simplified things by allowing programmers to have a choice
between two allocation functions: stack based types (structures), or
heap-allocated reference types (everything else).  By limiting the
choices they simplified the language, simplified run-time semantics, and
allowed for more general optimizations in the runtime.  (For example,
all copying can be done through bitwise copy operations, increasing
performance.)  Programming flexibility was less of a requirement than
simplification, that's all.

Thus we have one form of assignment -- bitwise copy -- with two sets of
semantics.  Structure assignment, which copies the whole structure, and
reference assignment, which copies a reference to the actual object.

As you put it, it's just a decision made by the folks who thought about
the language and the runtime.  I'm sure they had more reasons than the
above behind their decision, but it was still their decision.

    Finally, I want to thank you Jonathan, for your comments, I believe
    they helped
     
    With regards,
     
    Ricardo Kirkner

- Jon