Coffeehouse Thread

49 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

C#'s biggest mistake

Back to Forum: Coffeehouse
  • User profile image
    AndyC

    , BitFlipper wrote

    @W3bbo:

    Correct, I'm just trying to understand the problem that Sven identified.

    The 'partial' type syntax doesn't give you any way to say I want to extend a class from namespace A and I want the extension to be in namespace B. Which is basically an essential feature if you don't want to risk name collisions.

    It also creates a possibly awkward problem that you'd need to support properties extensions, operator extensions, event extensions and pretty much anything else you can think of that could be defined in a class extensions, or else the syntax will seem jarring (why does turning this into an extension suddenly break X, Y and Z)

  • User profile image
    W3bbo

    , AndyC wrote

    The 'partial' type syntax doesn't give you any way to say I want to extend a class from namespace A and I want the extension to be in namespace B. Which is basically an essential feature if you don't want to risk name collisions.

    But it does. In the example I posted the extension exists in MyNamespace1, but the class to be extended exists in MyNamespace2.

  • User profile image
    AndyC

    , W3bbo wrote

    *snip*

    But it does. In the example I posted the extension exists in MyNamespace1, but the class to be extended exists in MyNamespace2.

    Yes, but your example is how the real thing works and not the way BitFlipper was proposing it should have been done.

    Bass' suggestion is probably closer to what you'd have to do, but it looks wrong and too easily confused with inheritance.

  • User profile image
    BitFlipper

    , AndyC wrote

    *snip*

    The 'partial' type syntax doesn't give you any way to say I want to extend a class from namespace A and I want the extension to be in namespace B. Which is basically an essential feature if you don't want to risk name collisions.

    I just left out the namspace because I thought it was kind of obvious that you can resolve name collisions in exactly the same way you do right now with the current extension mechanism (or anywhere else in .Net for that matter). If the compiler complains about a name collision, simply add the namespace in front of the class you are extending. Why is this suddenly such a big problem?

    It also creates a possibly awkward problem that you'd need to support properties extensions, operator extensions, event extensions and pretty much anything else you can think of that could be defined in a class extensions, or else the syntax will seem jarring (why does turning this into an extension suddenly break X, Y and Z)

    The compiler will prevent you from doing things that don't make sense within the context of the extension class, like trying to add new fields, etc. How would this be any more "jarring" than what we have right now? Even if it only allows new methods and properties, it would already be an improvement over what we have now, and the syntax would be much less jarring than the current extension syntax.

    If you look at the "partial class" documentation, it lists a set of restrictions, and the compiler will let you know when you don't follow those restrictions. Why can a different set of restrictions not be applied in the case of an extension class? We aren't talking about just allowing you to add "extension" before a class, instead this is about adding new functionality to the compiler so that it is fully aware of such a new extension mechanism and its restrictions.

  • User profile image
    Sven Groot

    But this is not orthogonal and goes against the principle of least surprise. When I am creating what appears to be a new class definition, using your "extension class Foo" syntax, I would not expect the identifier to be resolved according to the using statements nor would I expect to be able to fully quality it. That is not the way things work for any other context in which you are defining a new type, so it would be jarring to have them work this way here.

    There is also the question of what the extension class would then actually be called (so that languages that don't support extension methods can access them as regular static methods), and how to deal with the possibility of the this parameter being null. For the latter, it seems it would be easy to say that you can just have the compiler insert a null check before calling the extension method, just like the JIT does for regular methods. But, again we must remember that extension methods are actually static methods and can be called as such, and in that case there's nothing stopping you from passing null, which the method would then not be prepared to handle. You can get around that by making extension methods a CLR feature, but then .Net 3.5 would not have been able to run on the 2.0 CLR.

    The null issue can be gotten around I guess by simply saying that in extension methods, this can be null, but it does feel a bit weird.

    How about this alternative:

    static class Extensions
    {
      public static void System.String.DoSomething(int x) { }
    }

    It's similar to the syntax used for explicit interface implementations, so it has precedent. Though admittedly it does also suffer from the "this can be null" issue.

  • User profile image
    BitFlipper

    @Sven Groot:

     

    I'm a little bit unsure as to why you are trying to overcomplicate things. What I am proposing is changing...

     

    public static class MyExtensions
    {
        public static int WordCount(this System.String str)
        {
            // ...
            return wordCount;
        }
    }

     

    ... to ...

     

    public extension class System.String
    {
        public int WordCount()
        {
            // ...
            return wordCount;
        }
    }
    

     

    It is merely a syntax change in how you write extensions to classes. Internally the compiler treats it exactly the same as it does right now, and emits exactly the same MSIL. Why would there suddenly be this list of new technical hurdles that do not exist in the current implementation?

     

    How does the current implementation deal with the "using" statement? How does the current implementation deal with a null this parameter? There are you answers.

     

    A long time ago there was a thread on C9 about missing extension properties, and IIRC, the reason given that there aren't any extension properties was that they could not come up with a satisfactory syntax for it. With the proposed syntax, it would be possible to add extension properties (and any other class features that make sense in the context of an extension class) in a known and familiar way that doesn't feel as contrived as what it apparently needs to be if it had to be added to the existing extension mechanism.

     

    In addition, the properties will just be compiled to the get_ and set_ methods anyway, so there should not be any problem with properties in this case either since the syntax issue with the current mechanism would be resolved.

     

    Can you clarify what the big problem is in this case?

  • User profile image
    exoteric

    @BitFlipper: You make a compelling case.

    Also note that extension methods can safely deal with nullness, which can be an asset, not a problem: consider this: string.IsNullOrWhitespace(myStr) vs myStr.IsNullOrWhitespace().

    The instance method cannot check for the presence of this-nullness (although that could be a neat feature to contemplate) but if IsNullOrWhitespace is an extension method it is entirely safe and in this case part of the job of the method to check for nullness.

    The general problem of nullness is not solved with extension methods but since all extension methods must deal with it anyway, in one way or another, it doesn't get worse either.

  • User profile image
    Sven Groot

    @BitFlipper: The problem is one of expectation. Consider a regular class:

     

    class Foo // this is the name of the class; it is scoped strictly
              // to the current namespace and cannot be fully qualified
    {
      public static int SomeMethod()
      {
        return this.Something; // this is guaranteed not to be null
      }
    }

     

    Now look at your syntax:

    extension class Foo // this is the no longer the name of the class (what would be?);
                        // it refers to a class in a different namespace but defines
                        // one in the current namespace and can suddenly be fully
                        // qualified, even though you can not normally do that in this context.
    {
      public static int SomeMethod()
      {
        return this.Something; // this can suddenly be null
      }
    }

     

    This hypothetical "extension" keyword changes the meaning of the name and the semantics of the method in a way that is inconsistent with how the language works elsewhere.

     

    By comparison, the actual current syntax doesn't change the meaning of a class declaration statement, and it makes it explicit that, regardless of how it may be invoked, the parameter that takes on the role of "this" is still a regular parameter and can therefore be null so you should check that and throw an ArgumentNullException if necessary.

  • User profile image
    BitFlipper

    @Sven Groot:

     

    Yes I see your point but why can't the compiler insert a null check before you even enter the extension method, just like it happens right now with regular methods? So if I have:

     

    string someString = null;
    someString.ToLower();    // <- NullReferenceException

     

    So if I do the same for an extension method, I should get:

     

    string someString = null;
    someString.MyExtensionMethod(); // <- NullReferenceException

     

    The way the current extension methods work is the one that has the problem with expectation. This is actually another opportunity to clean it up and make extension methods more consistent with regular methods and remove the problem with expectation that extension methods created in the first place.

  • User profile image
    Bass

    You are now starting to charge the semantics of extension methods to mean something more than syntax sugar over a static method. Basically, you want C# to support something like (run-time) Mixins.

     

    Here is an example from Ruby:

    http://donttrustthisguy.com/2005/12/31/ruby-extending-classes-and-method-chaining/

  • User profile image
    BitFlipper

    @Bass:

     

    Right now the compiler/IDE does everything it can to make extension methods appear like regular methods. Why is it doing that? The way it is currently implemented, it can't get there all the way. Why not go all the way and truly make them feel and act like regular methods? Are we so used to the discrepancies and limitations at this point that we should try and maintain the status quo? Or is there a use case where they are required to be as wonky as they are right now?

     

    Also, why does extension methods need to feel and act like static methods at all? Isn't that just a side-effect of how the current implementation works? You can even go so far and say that if you want a static method, you can use the "static" keyword just like you can with a regular method and it will feel and act just like a regular static method.

     

    EDIT: Obviously the compiler is well aware of extension methods as it currently implemented. Before extension methods were added, the same code would have refused to compile (what is this "this" thing before the type in the parameter list?). My point is that the compiler is already involved. I don't think the compiler support to make this suggestion work is necessarily much more complicated, just different. Yes I think there will probably be some more work for the compiler but the benefits of more consistency, familiarity for developers and new opportunities to add features like properties without contrived syntaxes outweigh the added complexity.  

  • User profile image
    Sven Groot

    @BitFlipper: But what about languages that don't directly support extension methods? They can still call them, but just as regular static methods. There would be no null-check inserted by those compilers.

     

    Unless of course you want that check to be added in the extension method itself, which is against going against expectation (slightly) because that's not where you'd expect the NullReferenceException to be thrown.

     

    And then there's languages  that don't have any syntax for extension methods (like VB). They can still declare them by decorating a method with the ExtensionAttribute, but of course the compiler won't add any extra stuff in those cases.

     

    Your syntax has merits, but I just feel it introduces too many corner cases and goes against how the language normally works in too many places. The only real way to get around that is to make extension methods a CLR feature rather than purely syntactic sugar.

  • User profile image
    Frank Hileman

    I believe the biggest weakness is the type system (a CLR error, not specifically a c# error). A derived type overriding a base type method should be able to weaken preconditions and strengthen postconditions in the overriding method. If we take preconditions and postconditions to include the argment types and return type of the method, an overriding method should be able to substitute base types for the type specified for an argument, and should be able to substitute a derived type for the return type.

     

    This would have solved problems such as the original IClonable interface problem. If I clone a Foo derived from ICloneable, the method Foo.Clone override could return a Foo instead of an Object, without needing to use a generic interface. When working with an instance typed as Foo, you would not need to downcast the returned clone as a Foo.

     

    Because it introduces new problems such how to do overloaded methods, it is a fundamental aspect of a type system that has to be figured out before a language is designed. I think it would have been a better type system, closer to the type hierarchy as described in the original Liskov "Abstraction and Specification in Program Design".

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.