Tech Off Thread

30 posts

Forum Read Only

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

Extension Methods - Too limited

Back to Forum: Tech Off
  • User profile image
    BitFlipper

    Yes this is another thread about extension methods, and how I believe they were borked. I feel that if the C# designers are going to add new features, they should decide to do it properly or not at all. This halfway working implementation only hurts the language.

     

    I'm in the middle of porting an old DirectX 9 3D engine from C++ to XNA C#. There is a lot of 3D math since it also does polygon-accurate collision detection. There are between 40,000 to 50,000 lines of code. This turned out to be a much bigger project than I anticipated because the differences between C++ and C# are much bigger than one would realize until doing something like this.

     

    Anyway, part of the porting complexity comes from the fact that I need to replace a lot of the existing classes with their XNA equivalents. For instance, I had a class called plane3. A plane is an infinite "plane" that divides a 3D space into two areas. It had constructors that take a "normal" and a 3D point on the plane. From these two vectors, I could construct a new plane. XNA has an equivalent called Plane, but it does not have an equivalent constructor.

     

    But as far as I can tell, you can't use extension methods to add constructors since you need an instance of a class or struct to call extension methods. So I need to create some other class that holds static methods that can construct a Plane from various parameters. If there was an ability to add true static extension methods, I could do something like:

     

    public static Plane.Create(Vector3 normal, Vector3 pointOnPlane) {}

     

    But I can't do that. What I can do though is add an extension method to Vector3 like this:

     

    public static Plane CreatePlane(this Vector3 thisNormal, Vector3 pointOnPlane) {}

     

    It feels wrong though - it should really be a method of Plane. Here's another example. I had a class called aabBox, which stands for axis aligned bounding box. The XNA equivalent is BoundingBox. I had a constructor that takes a single 3D point, which BoundingBox doesn't have. I also oveloaded the += operator so that I can add more 3D points and the bounding box will adjust to contain any newly added points. I was able to add an extension method like this:

     

    public static BoundingBox Union(this BoundingBox thisBb, Vector3 point) {}

     

    So in C++, the code looks like this:

     

    bb aabBox;
    for (int idx = 0; idx < numPoint; idx++)
        aabBox += points[idx];

     

    In C#, I have to do something like:

     

    bb = MyMath.EmptyBoundingBox;
    for (int idx = 0; idx < numPoints; idx++)
        bb = bb.Union(points[idx]);

     

    The reason I need to call to MyMath.EmptyBoundingBox is because the constructor for BoundingBox creates an instance with Min and Max points set to (0,0,0), which if you call Union on it with another point, will then include the (0,0,0) origin which is wrong. The 1st point added should be the one and only point withing the bounding box. So EmptyBoundingBox creats a special instance that has Min set to its max value, and Max set to its min value. In the Union call, it then calls my IsEmpty extension method to determine whether the instance is "empty" or not.

     

    And of course there is the lack of extension properties. Yes many people have complained about this but once again I find it strange that the reason it wasn't supposedly included is because they could not come up with the a functional syntax for this. So they went ahead anyway and created the extension syntax for methods, knowing full well that eventually they will add extension properties and that it will most likely conflict with the current extension property syntax. I don't think it is that hard to come up with syntax that would allow the following:

    • Extension instance methods (like we have now, except for structs it should be able to modify the actual struct values and not require a copy to be made, which results in the same struct being copied by value twice)
    • Extension static methods (for use like constructors etc)
    • Extension properties

    One way I was thinking of doing this in C# would be something like this:

     

    public extension class Plane
    {
        // Add whatever instance or static methods you want
        // Add any properties you want, but they can only reference existing member fields.
        // You can't add any new fields though...
    }

     

    This would be similar to how partial classes work, and its related syntax.

     

    While C# is a much more modern language than C++ in most ways, during this port I'm doing right now I find it surprising how much it feels as if in some ways, C# is a huge step backwards from C++.

     

    Are there better ways to do the things I'm trying to do? Am I missing something? Any help appreciated.

     

     

  • User profile image
    Dexter

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem? Yes, you'll have to use a different name like PlaneHelper but otherwise there's no difference, the syntax is the same.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    C# is a huge step backwards from C++.

     

    Well, some people will say it is, some people will say it's not. But you know what's funny in this context? That C++ doesn't have extensions methods at all Smiley

  • User profile image
    BitFlipper

    Dexter said:

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem? Yes, you'll have to use a different name like PlaneHelper but otherwise there's no difference, the syntax is the same.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    *snip*

     

    Well, some people will say it is, some people will say it's not. But you know what's funny in this context? That C++ doesn't have extensions methods at all Smiley

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem?

     

    The problem is that it makes the code less clean, which results in more difficulty reading/debugging/maintaining the code. The whole point is to make things clean and not more verbose than it needs to be.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    You missed my point that I am forced to add the extension method to the wrong type because I can't have static extension methods. That is when it doesn't make sense. That was the whole point of my post.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    That was just an example sice BoundingBox is easy to explain. I have many similar cases where there are no equivalent methods, like creating a plane from a normal and a point on the plane.

     

    But you know what's funny in this context? That C++ doesn't have extensions methods at all

     

     A question for you: Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like. Then you can cast any existing class to your derived class (as long as you don't add new member fields). The whole point of adding extension methods in C# is to work around these limitations that only exist in C# due to sealed classes, the inability to derive from structs, inability to cast to a derived class (the strongly typed nature is good, don't get me wrong though, but it places limitations like these on you), etc.

     

    So your point that it is "funny in this context" doesn't make any sense. Did I misunderstand you?

     

    BTW, I was contemplating creating an equivalent class where it has a instead of an is a. For instance, I have a class like this:

     

    public struct MyPlane
    {
        private Plane m_plane;
    
        public MyPlane(Plane fromPlane)
        { m_plane = fromPlane; }
    
        public MyPlane(Vector3 normal, Vector3 pointOnPlane)
        { ... }
    
        public Vector3 Normal 
        { get { return m_plane.Normal; } }
        { set { m_plane.Normal = value; } }
    
        // etc...
    }

     

    It still seems wrong though, and won't work in cases where I need to pass in arrays of such objects to methods that require arrays to the enclosed type.

  • User profile image
    Dexter

    BitFlipper said:
    Dexter said:
    *snip*

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem?

     

    The problem is that it makes the code less clean, which results in more difficulty reading/debugging/maintaining the code. The whole point is to make things clean and not more verbose than it needs to be.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    You missed my point that I am forced to add the extension method to the wrong type because I can't have static extension methods. That is when it doesn't make sense. That was the whole point of my post.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    That was just an example sice BoundingBox is easy to explain. I have many similar cases where there are no equivalent methods, like creating a plane from a normal and a point on the plane.

     

    But you know what's funny in this context? That C++ doesn't have extensions methods at all

     

     A question for you: Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like. Then you can cast any existing class to your derived class (as long as you don't add new member fields). The whole point of adding extension methods in C# is to work around these limitations that only exist in C# due to sealed classes, the inability to derive from structs, inability to cast to a derived class (the strongly typed nature is good, don't get me wrong though, but it places limitations like these on you), etc.

     

    So your point that it is "funny in this context" doesn't make any sense. Did I misunderstand you?

     

    BTW, I was contemplating creating an equivalent class where it has a instead of an is a. For instance, I have a class like this:

     

    public struct MyPlane
    {
        private Plane m_plane;
    
        public MyPlane(Plane fromPlane)
        { m_plane = fromPlane; }
    
        public MyPlane(Vector3 normal, Vector3 pointOnPlane)
        { ... }
    
        public Vector3 Normal 
        { get { return m_plane.Normal; } }
        { set { m_plane.Normal = value; } }
    
        // etc...
    }

     

    It still seems wrong though, and won't work in cases where I need to pass in arrays of such objects to methods that require arrays to the enclosed type.

    I'll ignore most of your comments and concentrate on a single one because I think it's extremly important:

     

    Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like.

     

    Deriving from a class and adding methods is not the same thing as extension methods. If that would be the case then there would be no extension methods in C#. It's perfectly possible to derive from List<T> and add a Where method but the result is no way near as useful as LINQ's Where extension method.

     

    Not only it's not the same thing but it's not exactly easy to do either. The Plane type has 4 constructors. If you inherit from Plane then you might need to add all those 4 constructors to MyPlane. The net result? You're actually writing more code.

     

  • User profile image
    wkempf

    BitFlipper said:
    Dexter said:
    *snip*

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem?

     

    The problem is that it makes the code less clean, which results in more difficulty reading/debugging/maintaining the code. The whole point is to make things clean and not more verbose than it needs to be.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    You missed my point that I am forced to add the extension method to the wrong type because I can't have static extension methods. That is when it doesn't make sense. That was the whole point of my post.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    That was just an example sice BoundingBox is easy to explain. I have many similar cases where there are no equivalent methods, like creating a plane from a normal and a point on the plane.

     

    But you know what's funny in this context? That C++ doesn't have extensions methods at all

     

     A question for you: Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like. Then you can cast any existing class to your derived class (as long as you don't add new member fields). The whole point of adding extension methods in C# is to work around these limitations that only exist in C# due to sealed classes, the inability to derive from structs, inability to cast to a derived class (the strongly typed nature is good, don't get me wrong though, but it places limitations like these on you), etc.

     

    So your point that it is "funny in this context" doesn't make any sense. Did I misunderstand you?

     

    BTW, I was contemplating creating an equivalent class where it has a instead of an is a. For instance, I have a class like this:

     

    public struct MyPlane
    {
        private Plane m_plane;
    
        public MyPlane(Plane fromPlane)
        { m_plane = fromPlane; }
    
        public MyPlane(Vector3 normal, Vector3 pointOnPlane)
        { ... }
    
        public Vector3 Normal 
        { get { return m_plane.Normal; } }
        { set { m_plane.Normal = value; } }
    
        // etc...
    }

     

    It still seems wrong though, and won't work in cases where I need to pass in arrays of such objects to methods that require arrays to the enclosed type.

    Wow!

     

    C# does NOT have extension methods "to work around not being able to inherit from sealed classes". Extension methods were created specifically for LINQ, and the types that were being extended there are not sealed types. Take a look throug the BCL and tell me how many sealed types you find, then tell me with a straight face that you think extension methods are meant to work around the "limitation" imposed by sealed types. BTW, extension methods would be quite beneficial in C++, as inheritance is does not provide the same capabilities.

     

    An "extension constructor" makes no sense to me. Even if you could find me a valid use case for this, I'm quite convinced it would be a corner case and as such shouldn't be accounted for in the language.

     

    The "extension class" concept is interesting and workable, but I hesitate to call it superior to extension methods, and I'm fine with extension methods as they exist.

     

    As for C# being inferior to C++, glad to know you have an opinion, but I'll respectfully disagree. I say that as someone who was a Boost contributor and long time C++ bigot.

  • User profile image
    exoteric

    There is a solution for you, it's called C++/CLI. Then you don't have to use C# at all.

     

    Now about the actual implementation of extension methods, I don't think leave all that much to be desired. It's always intellectually satisfying to have full generality but in practice, this is hardly something that has a super disruptive effect on code. Newing up objects have a slightly larger syntactic footprint in some instances, that's it. Hardly worth going up in flames about.

     

    It's actually a pretty wide field: extension methods (largest use-case for extensions), extension constructors, extension properties (save two characters), extension events, extension operators. Anders touched on this in a previous interview.

     

    Then we have other, more severe things, like no kinds, no interface-level operators, no numeric polymorphism. I think these have a much more dramatic and limiting impact on software design than the superficial difference between Statics.MyType(...) vs new MyType(...).

     

    I would like new languages to have fewer compromises and partial solutions. In that respect, Scala stands out, in my oppinion, as a really, really well thought out language, theoretically and practically.

  • User profile image
    BitFlipper

    Dexter said:
    BitFlipper said:
    *snip*

     

    Deriving from a class and adding methods is not the same thing as extension methods. If that would be the case then there would be no extension methods in C#. It's perfectly possible to derive from List<T> and add a Where method but the result is no way near as useful as LINQ's Where extension method.

     

    Not only it's not the same thing but it's not exactly easy to do either. The Plane type has 4 constructors. If you inherit from Plane then you might need to add all those 4 constructors to MyPlane. The net result? You're actually writing more code.

     

    Yes certainly extension methods and deriving from a class isn't exactly the same thing, but extension methods is a way to add some of the functionality back that you lost due to the nature of C#.

     

    It's perfectly possible to derive from List<T>...

     

    ...but it is impossible to derive from sealed classes and structs.

     

    The Plane type has 4 constructors. If you inherit from Plane then you might need to add all those 4 constructors to MyPlane. The net result? You're actually writing more code.

     

    Correct, but only in some way. I could do something like:

     

    public struct MyPlane
    {
        private Plane m_plane;
        public MyPlane(Plane fromPlane)
        { m_plane = fromPlane; }
    
        public static MyPlane FromPlane(Plane plane)
        { return new MyPlane(plane); }
    
        public Plane XnaPlane
        { get { return m_plane; } }
        { set { m_plane = value; } }
    
        // Methods and properties not available in Plane
        // ... 
    }

     

    With the above I can do anything I can with Plane (via the XnaPlane property), and I don't have to wrap existing constructors or methods because I can do stuff like:

     

    var myPlane = new MyPlane(new Plane(...));

     

    or

     

    var intersectType = myPlane.XnaPlane.Intersect(myBoundingSphere);

     

    Once again it seems a bit hacky but not any more than having extension methods for Plane plus another set of methods in a different class because I can't add them to Plane via extension methods because of the imposed limitations.

     

    Hmmm... Which one of the non-ideal solutions to pick from...???  This seems like such a silly thing but when you do a port from C++ to C# these things become a PITA. You are forced to come up with various solutions, none of which a re perfect, and all of them seem like a hack in one way or another.

     

    The more I think about it, the more I think this would have been the perfect solution:

     

    public extension struct Plane
    {
        // Add whatever instance or static methods you want, including constructors
        // Add any properties you want, but they can only reference existing member fields.
        // You can't add any new fields...
    }

     

    Then I can do this:

     

    public extension struct Plane
    {
        public void BringToPoint(Vector3 point)
        {
            this.D = -Vector3.Dot(this.Normal, point));
        }
    }

     

    Which is already cleaner than:

     

    public static class Extensions
    {
        public static Plane BringToPoint(this Plane plane, Vector3 point)
        {
            return new Plane(plane.Normal, -Vector3.Dot(plane.Normal, point));
        }
    }

     

    ...and faster too since it works directly on the member fields instead of requiring multiple pass-by-value operations.

     

    Then I can do:

     

    myPlane.BringToPoint(somePoint);

     

    ...instead of...

     

    myPlane = myPlane.BringToPoint(somePoint);

     

    The current extension syntax seems overly engineered, convoluted and limited in how it can be expanded in the future to include static methods and properties for instance. Having a syntax like "extension class SomeClass {}" or "extension struct SomeStruct {}" seems much cleaner and has no limitations on future expandability, at least to me. You don't have to add the "this" keyword as the first parameter since the compiler will know exactly what is going on since there are no ambiguities. It is all wrapped within the "extension class/struct" block.

  • User profile image
    BitFlipper

    wkempf said:
    BitFlipper said:
    *snip*

    Wow!

     

    C# does NOT have extension methods "to work around not being able to inherit from sealed classes". Extension methods were created specifically for LINQ, and the types that were being extended there are not sealed types. Take a look throug the BCL and tell me how many sealed types you find, then tell me with a straight face that you think extension methods are meant to work around the "limitation" imposed by sealed types. BTW, extension methods would be quite beneficial in C++, as inheritance is does not provide the same capabilities.

     

    An "extension constructor" makes no sense to me. Even if you could find me a valid use case for this, I'm quite convinced it would be a corner case and as such shouldn't be accounted for in the language.

     

    The "extension class" concept is interesting and workable, but I hesitate to call it superior to extension methods, and I'm fine with extension methods as they exist.

     

    As for C# being inferior to C++, glad to know you have an opinion, but I'll respectfully disagree. I say that as someone who was a Boost contributor and long time C++ bigot.

    @wkempf

     

    The documentation states:

     

    Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

     

    While I don't disagree that one of the main purposes were to support LINQ, the documentation clearly speaks directly to what I am talking about.

     

    An "extension constructor" makes no sense to me. Even if you could find me a valid use case for this, I'm quite convinced it would be a corner case and as such shouldn't be accounted for in the language.

     

    I already gave a perfectly good example. the Plane class does not have a constructor that takes a normal and a point-on-the-plane. You might think it isn't important since you don't do this kind of programming, but I use this all over my code, and I also have examples of other classes or structs that have similar missing equivalent constructors. I would hardly call any of these corner cases. Why is it that when someone doesn't run into an issue themselves do they assume it can't possibly affect anybody else either?

     

    The "extension class" concept is interesting and workable, but I hesitate to call it superior to extension methods, and I'm fine with extension methods as they exist.

     

    That's the problem. It works fine for you. That doesn't mean other people might not have different requirements.

     

    As for C# being inferior to C++, glad to know you have an opinion, but I'll respectfully disagree. I say that as someone who was a Boost contributor and long time C++ bigot.

     

    I think you misunderstood me. I love C#. Way more than any other language. Why do you think I tackled a project like this? Because I'd love to see what I can do with a C# 3D engine, as well as exploring the possibility of using this engine on WP7. I am merely venting about what I feel to be a limitation in an otherwise great language. Isn't that how we can come up with solutions that could make the language better for everyone?

     

     

    @exoteric

     

    There is a solution for you, it's called C++/CLI. Then you don't have to use C# at all.

     

    That is not a solution at all. Especially since XNA doesn't allow P/Invoking whatsoever (for unmanaged code), and also has no support for anything other than C# (so no C++/CLI support). And I think you also got the idea that I don't like C#. That is not the case at all.

     

    I would like new languages to have fewer compromises and partial solutions

     

    But that is exactly what I am talking about here. My feeling is that extension methods have too many compromises and is only a partial solution. It could have been implemented cleaner and more functionally complete, compared to what we have now. Even if it gets "fixed" in later versions, we are stuck with the current syntax due to the backwards compatible requirement.

  • User profile image
    BitFlipper

    Sorry for the somewhat "negative" tone. It is just that when you try and port a large (well, large for one person) project from C++ to C#, these small details become big issues. This is causing me to have to re-write a lot of code because I can't even get close to how the C++ version used to work (which IMO seems cleaner in some ways). Just sayin that it could have been easier if extension methods were implemented more effectively.

  • User profile image
    wkempf

    BitFlipper said:
    wkempf said:
    *snip*

    @wkempf

     

    The documentation states:

     

    Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

     

    While I don't disagree that one of the main purposes were to support LINQ, the documentation clearly speaks directly to what I am talking about.

     

    An "extension constructor" makes no sense to me. Even if you could find me a valid use case for this, I'm quite convinced it would be a corner case and as such shouldn't be accounted for in the language.

     

    I already gave a perfectly good example. the Plane class does not have a constructor that takes a normal and a point-on-the-plane. You might think it isn't important since you don't do this kind of programming, but I use this all over my code, and I also have examples of other classes or structs that have similar missing equivalent constructors. I would hardly call any of these corner cases. Why is it that when someone doesn't run into an issue themselves do they assume it can't possibly affect anybody else either?

     

    The "extension class" concept is interesting and workable, but I hesitate to call it superior to extension methods, and I'm fine with extension methods as they exist.

     

    That's the problem. It works fine for you. That doesn't mean other people might not have different requirements.

     

    As for C# being inferior to C++, glad to know you have an opinion, but I'll respectfully disagree. I say that as someone who was a Boost contributor and long time C++ bigot.

     

    I think you misunderstood me. I love C#. Way more than any other language. Why do you think I tackled a project like this? Because I'd love to see what I can do with a C# 3D engine, as well as exploring the possibility of using this engine on WP7. I am merely venting about what I feel to be a limitation in an otherwise great language. Isn't that how we can come up with solutions that could make the language better for everyone?

     

     

    @exoteric

     

    There is a solution for you, it's called C++/CLI. Then you don't have to use C# at all.

     

    That is not a solution at all. Especially since XNA doesn't allow P/Invoking whatsoever (for unmanaged code), and also has no support for anything other than C# (so no C++/CLI support). And I think you also got the idea that I don't like C#. That is not the case at all.

     

    I would like new languages to have fewer compromises and partial solutions

     

    But that is exactly what I am talking about here. My feeling is that extension methods have too many compromises and is only a partial solution. It could have been implemented cleaner and more functionally complete, compared to what we have now. Even if it gets "fixed" in later versions, we are stuck with the current syntax due to the backwards compatible requirement.

    "Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.

     

    While I don't disagree that one of the main purposes were to support LINQ, the documentation clearly speaks directly to what I am talking about."

     

    That's not true at all. In LINQ they are doing exactly what the documentation is talking about here, and I again point out that the types involved are not sealed. The purpose of extension methods isn't to work around "limitations" imposed by sealed classes. No, the purpose is exactly what the documentation said: to enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Again, even in C++ where there's no such thing as a sealed class, extension methods would still be extremely beneficial. Further, extension methods do NOT provide any real benefit with sealed classes, because they do NOT allow you to provide polymorphic behavior. Read any of the books on C++ design and see what their opinion of inheriting from a class just to add functionality is. Specifically, read what people say about inheriting from std:basic_string just to add some string manipulation function. In C++ this sort of behavior is actually much WORSE. C++ doesn't have sealed classes, but they DO have classes that don't have virtual destructors. It's perfectly legal to inherit from such classes, but the results can be catastrophic if you delete the instance through a base pointer, and as such your strongly discouraged from inheriting from such types. C++ would be better off if it had sealed classes and extension methods.

     

    My real problem here isn't with your wanting more expressivity via an "extension class" instead of using extension methods. My problem is with your harsh criticism with extremely flawed logic. The extension class idea might be a good idea, though I'd have to analyze the details, and MSFT might have already done so and determined extension methods were safer. But there's nothing flawed about extension methods as they exist, and they certainly weren't designed to fix any "flaws" in the language, especially not flaws that exist in C# but not in C++.

  • User profile image
    wkempf

    BitFlipper said:

    Sorry for the somewhat "negative" tone. It is just that when you try and port a large (well, large for one person) project from C++ to C#, these small details become big issues. This is causing me to have to re-write a lot of code because I can't even get close to how the C++ version used to work (which IMO seems cleaner in some ways). Just sayin that it could have been easier if extension methods were implemented more effectively.

    Considering C++ doesn't have extension methods at all, it seems you must be making some bad decisions in how your porting the C++. To be helpful, I question the need for an extension constructor at all, due to having initialization statements in C#. If you have a really complex initialization pattern that used frequently, that may not be enough, but in such situations other solutions exist, such as using factories (often a better design in the first place), utility methods (only slightly more verbose, and not worth ranting about), extension methods used to convert from one type to another (such as the ToList() methods in LINQ), and composition (usually a better choice than inheritance in the first place).

  • User profile image
    Sven Groot

    BitFlipper said:
    Dexter said:
    *snip*

    Well, I can agree about properties but I don't see the point in having static extension methods. You say you have to create another class to hold the static methods. So, what's the problem?

     

    The problem is that it makes the code less clean, which results in more difficulty reading/debugging/maintaining the code. The whole point is to make things clean and not more verbose than it needs to be.

     

    You'll likely have to create some "helper" classes anyway because the XNA math library is not exactly complete and as you noticed yourself it doesn't always make sense to add an extension method to one of the existing types.

     

    You missed my point that I am forced to add the extension method to the wrong type because I can't have static extension methods. That is when it doesn't make sense. That was the whole point of my post.

     

    BoundingBox - there's a CreateFromPoints that takes an IEnumerable<Vector3>. Can't you use that instead your for loop?

     

    That was just an example sice BoundingBox is easy to explain. I have many similar cases where there are no equivalent methods, like creating a plane from a normal and a point on the plane.

     

    But you know what's funny in this context? That C++ doesn't have extensions methods at all

     

     A question for you: Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like. Then you can cast any existing class to your derived class (as long as you don't add new member fields). The whole point of adding extension methods in C# is to work around these limitations that only exist in C# due to sealed classes, the inability to derive from structs, inability to cast to a derived class (the strongly typed nature is good, don't get me wrong though, but it places limitations like these on you), etc.

     

    So your point that it is "funny in this context" doesn't make any sense. Did I misunderstand you?

     

    BTW, I was contemplating creating an equivalent class where it has a instead of an is a. For instance, I have a class like this:

     

    public struct MyPlane
    {
        private Plane m_plane;
    
        public MyPlane(Plane fromPlane)
        { m_plane = fromPlane; }
    
        public MyPlane(Vector3 normal, Vector3 pointOnPlane)
        { ... }
    
        public Vector3 Normal 
        { get { return m_plane.Normal; } }
        { set { m_plane.Normal = value; } }
    
        // etc...
    }

     

    It still seems wrong though, and won't work in cases where I need to pass in arrays of such objects to methods that require arrays to the enclosed type.

    A question for you: Why do you think C++ doesn't have extension methods? That's because C++ doesn't require extension methods to begin with. You can derive from any C++ class or struct and add whatever you like. Then you can cast any existing class to your derived class (as long as you don't add new member fields).

    I really must object to doing this. For one thing, it's only safe as long as the derived class has no member fields and exactly one base class. For another, the results of such a conversion are specifically stated as being undefined in the C++ standard for all cast types (except dynamic_cast, where such a cast is required to fail). The fact that this works at all is therefore an implementation detail of the specific compiler you're using and not guaranteed to be portable to different compilers or different versions of the same compiler.

  • User profile image
    BitFlipper

    OK fine. In a lot of cases you guys are arguing semantics. Let's agree then that extension methods' main purpose is to support LINQ, and that anything else is icing on the cake. Extension methods can be useful to add required functionality to classes or structs, whether sealed or not. Which is what I'm using it for.

     

    @wkempf

     

    C++ doesn't have sealed classes, but they DO have classes that don't have virtual destructors. It's perfectly legal to inherit from such classes, but the results can be catastrophic if you delete the instance through a base pointer, and as such your strongly discouraged from inheriting from such types.

     

    That is why I said that as long as you don't add any new member fields in a C++ class, it will "work", although I agree it is not an ideal solution and that it should probably be discouraged. I was merely pointing out that you can do this in C++. In my own C++ project I never did something like that though since all the C++ classes were my own (see below).

     

    Considering C++ doesn't have extension methods at all, it seems you must be making some bad decisions in how your porting the C++.

     

    No this is not a case of bad decisions in porting this from C++. In the C++ version, the class I'm starting with is my own class, so it never extended any existing classes. In the C# version, I need to use the built-in XNA classes and structs. Unfortunately the XNA versions have quite a bit less functionality than my C++ versions. So the problem is that I have to spread out the existing functionality over different classes. I am adding what I can via extension methods, and the rest has to go into utility classes, which is not ideal because I would at least like to keep similar functionality that is all related to the same class or struct in the same place. So I fail to see how this is due to "bad decisions". How would you do this any differently? I'm really open to suggestions on what the best way is to deal with this, keeping in mind that it would help out a lot the less I need to manually refactor existing code since it takes a very long time, and it is more likely to introduce new bugs when doing so.

     

    To be helpful, I question the need for an extension constructor at all, due to having initialization statements in C#. If you have a really complex initialization pattern that used frequently, that may not be enough, but in such situations other solutions exist, such as using factories (often a better design in the first place), utility methods...

     

    Here is my example again: My original C++ Plane class has a constructor that takes 2 points. It is a "normal" and a point on the plane. This is a very common way to create a plane, yet there is no equivalent XNA Plane constructor. This is not a "really complex initialization pattern" as you say. So now I need to move this off to a utility class. If it was possible to add a constructor as an extension method, then porting the code would have been easier and the functionality would have been part of the Plane class, not a utility class. Or at least if I could add static extension methods, then I can do it factory style, something like "var myPlane = Plane.Create(...)" or something similar, but that can't be done either because static extension methods aren't allowed. All of these XNA structs have lots of static methods, so I don't see why wanting to add my own where it makes sense is such a bad thing. If static methods were inherently bad, why were they added to the original classes/structs?

     

    Once again, this seems like minor details to be complaining about, but when you are porting a sizeable project from C++ to C#, these things become a big source of complications. I have probably introduced a large amount of new bugs simply due to having to refactor many parts of the code because of this issue.

     

    I guess I can understand why some of you have a problem with my POV (I think I come across as more "ranting" and "attacking" than what the intension is), although I am surprised that no-one seems to think that it would have been more helpful if extension methods (and properties) were more flexible than they are now. When you start out with a C# project, then I guess if you stay within the limitations, it doesn't make much sense to require much more, but from a C++ to C# POV, it seems that more flexibility could help out a lot.

  • User profile image
    Sven Groot

    BitFlipper said:

    OK fine. In a lot of cases you guys are arguing semantics. Let's agree then that extension methods' main purpose is to support LINQ, and that anything else is icing on the cake. Extension methods can be useful to add required functionality to classes or structs, whether sealed or not. Which is what I'm using it for.

     

    @wkempf

     

    C++ doesn't have sealed classes, but they DO have classes that don't have virtual destructors. It's perfectly legal to inherit from such classes, but the results can be catastrophic if you delete the instance through a base pointer, and as such your strongly discouraged from inheriting from such types.

     

    That is why I said that as long as you don't add any new member fields in a C++ class, it will "work", although I agree it is not an ideal solution and that it should probably be discouraged. I was merely pointing out that you can do this in C++. In my own C++ project I never did something like that though since all the C++ classes were my own (see below).

     

    Considering C++ doesn't have extension methods at all, it seems you must be making some bad decisions in how your porting the C++.

     

    No this is not a case of bad decisions in porting this from C++. In the C++ version, the class I'm starting with is my own class, so it never extended any existing classes. In the C# version, I need to use the built-in XNA classes and structs. Unfortunately the XNA versions have quite a bit less functionality than my C++ versions. So the problem is that I have to spread out the existing functionality over different classes. I am adding what I can via extension methods, and the rest has to go into utility classes, which is not ideal because I would at least like to keep similar functionality that is all related to the same class or struct in the same place. So I fail to see how this is due to "bad decisions". How would you do this any differently? I'm really open to suggestions on what the best way is to deal with this, keeping in mind that it would help out a lot the less I need to manually refactor existing code since it takes a very long time, and it is more likely to introduce new bugs when doing so.

     

    To be helpful, I question the need for an extension constructor at all, due to having initialization statements in C#. If you have a really complex initialization pattern that used frequently, that may not be enough, but in such situations other solutions exist, such as using factories (often a better design in the first place), utility methods...

     

    Here is my example again: My original C++ Plane class has a constructor that takes 2 points. It is a "normal" and a point on the plane. This is a very common way to create a plane, yet there is no equivalent XNA Plane constructor. This is not a "really complex initialization pattern" as you say. So now I need to move this off to a utility class. If it was possible to add a constructor as an extension method, then porting the code would have been easier and the functionality would have been part of the Plane class, not a utility class. Or at least if I could add static extension methods, then I can do it factory style, something like "var myPlane = Plane.Create(...)" or something similar, but that can't be done either because static extension methods aren't allowed. All of these XNA structs have lots of static methods, so I don't see why wanting to add my own where it makes sense is such a bad thing. If static methods were inherently bad, why were they added to the original classes/structs?

     

    Once again, this seems like minor details to be complaining about, but when you are porting a sizeable project from C++ to C#, these things become a big source of complications. I have probably introduced a large amount of new bugs simply due to having to refactor many parts of the code because of this issue.

     

    I guess I can understand why some of you have a problem with my POV (I think I come across as more "ranting" and "attacking" than what the intension is), although I am surprised that no-one seems to think that it would have been more helpful if extension methods (and properties) were more flexible than they are now. When you start out with a C# project, then I guess if you stay within the limitations, it doesn't make much sense to require much more, but from a C++ to C# POV, it seems that more flexibility could help out a lot.

    Considering the scenario you describe here, if the difference in functionality is really that significant, I'd just create my own Plane class that either encapsulates XNA's plane class or can be converted to it. Otherwise you're just fighting an uphill battle.

  • User profile image
    Dexter

    BitFlipper said:

    OK fine. In a lot of cases you guys are arguing semantics. Let's agree then that extension methods' main purpose is to support LINQ, and that anything else is icing on the cake. Extension methods can be useful to add required functionality to classes or structs, whether sealed or not. Which is what I'm using it for.

     

    @wkempf

     

    C++ doesn't have sealed classes, but they DO have classes that don't have virtual destructors. It's perfectly legal to inherit from such classes, but the results can be catastrophic if you delete the instance through a base pointer, and as such your strongly discouraged from inheriting from such types.

     

    That is why I said that as long as you don't add any new member fields in a C++ class, it will "work", although I agree it is not an ideal solution and that it should probably be discouraged. I was merely pointing out that you can do this in C++. In my own C++ project I never did something like that though since all the C++ classes were my own (see below).

     

    Considering C++ doesn't have extension methods at all, it seems you must be making some bad decisions in how your porting the C++.

     

    No this is not a case of bad decisions in porting this from C++. In the C++ version, the class I'm starting with is my own class, so it never extended any existing classes. In the C# version, I need to use the built-in XNA classes and structs. Unfortunately the XNA versions have quite a bit less functionality than my C++ versions. So the problem is that I have to spread out the existing functionality over different classes. I am adding what I can via extension methods, and the rest has to go into utility classes, which is not ideal because I would at least like to keep similar functionality that is all related to the same class or struct in the same place. So I fail to see how this is due to "bad decisions". How would you do this any differently? I'm really open to suggestions on what the best way is to deal with this, keeping in mind that it would help out a lot the less I need to manually refactor existing code since it takes a very long time, and it is more likely to introduce new bugs when doing so.

     

    To be helpful, I question the need for an extension constructor at all, due to having initialization statements in C#. If you have a really complex initialization pattern that used frequently, that may not be enough, but in such situations other solutions exist, such as using factories (often a better design in the first place), utility methods...

     

    Here is my example again: My original C++ Plane class has a constructor that takes 2 points. It is a "normal" and a point on the plane. This is a very common way to create a plane, yet there is no equivalent XNA Plane constructor. This is not a "really complex initialization pattern" as you say. So now I need to move this off to a utility class. If it was possible to add a constructor as an extension method, then porting the code would have been easier and the functionality would have been part of the Plane class, not a utility class. Or at least if I could add static extension methods, then I can do it factory style, something like "var myPlane = Plane.Create(...)" or something similar, but that can't be done either because static extension methods aren't allowed. All of these XNA structs have lots of static methods, so I don't see why wanting to add my own where it makes sense is such a bad thing. If static methods were inherently bad, why were they added to the original classes/structs?

     

    Once again, this seems like minor details to be complaining about, but when you are porting a sizeable project from C++ to C#, these things become a big source of complications. I have probably introduced a large amount of new bugs simply due to having to refactor many parts of the code because of this issue.

     

    I guess I can understand why some of you have a problem with my POV (I think I come across as more "ranting" and "attacking" than what the intension is), although I am surprised that no-one seems to think that it would have been more helpful if extension methods (and properties) were more flexible than they are now. When you start out with a C# project, then I guess if you stay within the limitations, it doesn't make much sense to require much more, but from a C++ to C# POV, it seems that more flexibility could help out a lot.

    I think I come across as more "ranting" and "attacking" than what the intension is

     

    You came across as more "whining". I know both C++ and C# and I have enough familiarity with 3D graphics to tell that converting 40000-50000 lines of code is not going to be easy. But I fail to see how these missing methods can be such a problem.

     

    What about the fact that C# has garbage collection instead of manual memory management?

    What about the fact that in C# there's a distinction between "value types" and "reference types"? C++ doesn't have such a thing.

    What about the fact that C++ templates are not the same thing as C# generics?

    Or about the fact that you're trying to convert from Direct3D9 to XNA 4 which has a graphics API that's closer to Direct3D10?

     

  • User profile image
    wkempf

    Sven Groot said:
    BitFlipper said:
    *snip*

    Considering the scenario you describe here, if the difference in functionality is really that significant, I'd just create my own Plane class that either encapsulates XNA's plane class or can be converted to it. Otherwise you're just fighting an uphill battle.

    Which was one of my suggestions (composition) and the one I'd likely employ as well.

     

    Yeah, BitFlipper, the responses (at least mine) were due to the tone. You ranted, fairly harshly, and compared C# to C++ critically, with criticism that didn't hold water. So, you got my responses Smiley. It's also true that you're in a bit of a unique situation. You're porting existing code from another language. This always entails "rough spots" that simply don't crop up in normal development. Like I've said a couple of times, barring technical issues I've not thought of because I just don't care to analyze it, your extension class idea seems like a fairly decent idea, but I also don't think it adds all that much more than extension methods. At least, what it does add isn't functionality that would be used that often.

     

    BTW, similar to the point Sven made about casting, deleting a derived class through a base pointer when the base type does not have a virtual destructor results in undefined behavior. The claim about it being "safe" if you don't add any fields isn't really true. At least, it's not accurate. It's true for many compiler implementations, but it's not true according to the language specification, which is all that should matter.

  • User profile image
    spivonious

    The real solution here is to derive your own Plane class. It can still be passed everywhere a Plane is expected, yet you'll gain the new constructors and methods that you need. Isn't this the whole point of object-oriented programming?

  • User profile image
    BitFlipper

    Dexter said:
    BitFlipper said:
    *snip*

     

    You came across as more "whining". I know both C++ and C# and I have enough familiarity with 3D graphics to tell that converting 40000-50000 lines of code is not going to be easy. But I fail to see how these missing methods can be such a problem.

     

    What about the fact that C# has garbage collection instead of manual memory management?

    What about the fact that in C# there's a distinction between "value types" and "reference types"? C++ doesn't have such a thing.

    What about the fact that C++ templates are not the same thing as C# generics?

    Or about the fact that you're trying to convert from Direct3D9 to XNA 4 which has a graphics API that's closer to Direct3D10?

     

    @Dexter

     

    Yes you are 100% correct. The issues you mention are all things that makes such a port difficult. Most of my C++ classes/structs like Plane, BoundingBox, Vector3, Frustum, etc contain 3D math that don't need to interact with the 3D API, and as such in theory at least, it should be a relatively "clean" port. Unfortunately I need to split the code up into what can and what cannot be put into extension methods. That results in code that is spread out when ideally it should be kept together.

     

    As far as using composition, I am trying it now with the Plane class, and I think it is a workable solution. I can implement implicit casts to/from my Plane class and the XNA Plane class. But it doesn't work in all cases. For instance, for the XNA Vector3 struct, you often need to pass in arrays of them into the XNA API. So composition is not going to work in that case.

     

    Another problem with extension methods (and I am sure some people will say it's a non-issue), is that with value types there is overhead when using an extension method. Take the Matrix class, for instance. It has 16 float values, which consists of 64 bytes. I want to have a Matrix extension method called BringToPlane where the location of the matrix is adjusted so that its origin is on the plane (once again, this is just an example, I have many such cases). With an extension method, the whole structure is copied by value into the extension method, and afterwards it needs to return the whole structure again, even though only 3 of its 16 float values changed. Ideally I should be able to pass the structure by ref, but I can't do it with an extension method. If you look at the Matrix class, you'll see it contains methods that pass other structs by ref, so obviously this is an issue. In a 3D engine things like this add up since you do these operations in tight loops.

     

    @spinovious

     

    The real solution here is to derive your own Plane class. It can still be passed everywhere a Plane is expected, yet you'll gain the new constructors and methods that you need. Isn't this the whole point of object-oriented programming?

     

    In this case I can't derive since Plane is a struct. So is Vector3, Matrix (64 bytes!), Quaternion, BoundingBox, etc. Extension methods do allow me to get partially what I need though, but not all the way.

Conversation locked

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