Slow performance compared to C++, ideas?
Steven Schveighoffer
schveiguy at yahoo.com
Wed Jun 5 16:29:12 PDT 2013
On Wed, 05 Jun 2013 18:56:28 -0400, Walter Bright
<newshound2 at digitalmars.com> wrote:
> On 6/5/2013 3:37 PM, Steven Schveighoffer wrote:
>> No, I think it introduces a new foo. Calling A.foo does not call
>> B.foo. In
>> other words, it hides the original implementation, there are two vtable
>> entries
>> for foo.
>>
>> At least, that is how I understood the C# description from that post,
>> and it
>> seems Walter is trying to specify that. The idea is that B probably
>> defined foo
>> before A did, and A adding foo should not break B, B didn't even know
>> about A's
>> foo.
>
> That's right.
>
Prompted by Paulo's reference, I see there is another twist not clear from
the interview article.
I think it is important to examine the *exact* semantics of C# so we can
judge which parts to have.
Here is a complete excerpt from the ECMA submission for C#, which I think
is very informative (bear with the unicode errors, this is copy pasted
from this document:
http://www.ecma-international.org/publications/files/ECMA-ST-WITHDRAWN/ECMA-334,%202nd%20edition,%20December%202002.pdf
):
8.13 Versioning
Versioning is the process of evolving a component over time in a
compatible manner. A new version of a component is source compatible with
a previous version if code that depends on the previous version can, when
recompiled, work with the new version. In contrast, a new version of a
component is binary compatible if an application that depended on the old
version can, without recompilation, work with the new version.
Most languages do not support binary compatibility at all, and many do
little to facilitate source compatibility. In fact, some languages contain
flaws that make it impossible, in general, to evolve a class over time
without breaking at least some client code.
As an example, consider the situation of a base class author who ships a
class named Base. In the first version, Base contains no method F. A
component named Derived derives from Base, and introduces an F. This
Derived class, along with the class Base on which it depends, is released
to customers, who deploy to numerous clients and servers.
// Author A
namespace A
{
public class Base // version 1
{
}
}
// Author B
namespace B
{
class Derived: A.Base
{
public virtual void F() {
System.Console.WriteLine("Derived.F");
}
}
}
So far, so good, but now the versioning trouble begins. The author of Base
produces a new version, giving it its own method F.
// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() // added in version 2
{
System.Console.WriteLine("Base.F");
}
}
}
This new version of Base should be both source and binary compatible with
the initial version. (If it werenít possible to simply add a method then
a base class could never evolve.) Unfortunately, the new F in Base makes
the meaning of Derivedís F unclear. Did Derived mean to override Baseís
F? This seems unlikely, since when Derived was compiled, Base did not even
have an F! Further, if Derivedís F does override Baseís F, then it must
adhere to the contract specified by Baseóa contract that was unspecified
when Derived was written. In some cases, this is impossible. For example,
Baseís F might require that overrides of it always call the base.
Derivedís F could not possibly adhere to such a contract.
C# addresses this versioning problem by requiring developers to state
their intent clearly. In the original code example, the code was clear,
since Base did not even have an F. Clearly, Derivedís F is intended as a
new method rather than an override of a base method, since no base method
named F exists.
If Base adds an F and ships a new version, then the intent of a binary
version of Derived is still clearó Derivedís F is semantically
unrelated, and should not be treated as an override.
However, when Derived is recompiled, the meaning is unclearóthe author of
Derived may intend its F to override Baseís F, or to hide it. Since the
intent is unclear, the compiler produces a warning, and by default makes
Derivedís F hide Baseís F. This course of action duplicates the
semantics for the case in which Derived is not recompiled. The warning
that is generated alerts Derivedís author to the presence of the
F method in Base.
If Derived's F is semantically unrelated to Baseís F, then Derivedís
author can express this intentóand,
in effect, turn off the warningóby using the new keyword in the
declaration of F.
// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() // added in version 2
{
System.Console.WriteLine("Base.F");
}
}
}
// Author B
namespace B
{
class Derived: A.Base // version 2a: new
{
new public virtual void F() {
System.Console.WriteLine("Derived.F");
}
}
}
On the other hand, Derivedís author might investigate further, and decide
that Derivedís F should override Baseís F. This intent can be specified
by using the override keyword, as shown below.
// Author A
namespace A
{
public class Base // version 2
{
public virtual void F() // added in version 2
{
System.Console.WriteLine("Base.F");
}
}
}
// Author B
namespace B
{
class Derived: A.Base // version 2b: override
{
public override virtual void F() {
base.F();
System.Console.WriteLine("Derived.F");
}
}
}
The author of Derived has one other option, and that is to change the name
of F, thus completely avoiding the name collision. Although this change
would break source and binary compatibility for Derived, the importance of
this compatibility varies depending on the scenario. If Derived is not
exposed to other programs, then changing the name of F is likely a good
idea, as it would improve the readability of the programóthere would no
longer be any confusion about the meaning of F.
More information about the Digitalmars-d
mailing list