[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