The dream to inherit from a struct in c#
Asked Answered
J

3

6

There I am making a 2D game in C# XNA 4.0, and run across yet again a petty annoyance of mine; the Rectangle. For those using basic collision, this is almost a necessity. For almost any game object created you need to have a rectangle. Then I go to change the X, check collision, or anything else. And there I begin the never-ending battle of objectName.Rectangle.Whatever. To get around this I of course give the class of objectName properties/methods that access these for me.

Then I dared to dream. I had grand designs to make a basic game object class that everything drawable would inherit from, that would allow parenting, precise local coordinates (floats), and hold the texture/spritebatch. To make this complete I was ready to inherit from Rectangle, to use all the methods and attributes that it holds. Heck, whenever something required a Rectangle, I could be so lazy as to say objectName, not objectName.Rectangle.

Then I realized, not a chance. I started out depressed as my oh-so-clever idea was smashed to bits. Since then my perfect little class holds a rectangle, with various methods and properties accessing it as needed. I have also taken the chance to have it inherit from the XNA DrawableGameComponent. While in the long run this has been more practical, every time I view a draw or update method and see the call to rectangle I often wonder, was there ever a hope to do what I had wanted? Was there some clever work around that I could have done? Or was inheriting from a Rectangle truly sealed from my grasp?

While using the DrawableGameComponent class provided in XNA allows most game-object related actions happen inside the classes Update() method, every time outside of a class I need to reference not to property of a Rectangle, but rather the Rectangle itself, I am slightly peeved considering that in really every way my object is, in fact, and souped-up Rectangle. And then once again I can find myself asking:

Is there any way to inherit from a pre-defined struct, or give the project the impression you are (a work-around)?

Jago answered 16/6, 2012 at 17:42 Comment(2)
Duplicate of #2310603. Structs are supposed to be value types, the EMCA spec lists structs as sealed to avoid value splicing and to create a more efficient implementation.Fossil
@RobertRouhani That's asking 'why', I'm asking 'how'. Two different questions.Jago
F
16

Inherit no, but you can add a lot of 'default' functionality to the Rectangle object with extension methods. For example

  //(inside a static class)
  public static int GetSurface(this Rectangle rect){return rect.Width * rect.Height;}

  //calling
  Rectangle rect;
  var s = rect.GetSurface();

That said, what I normally do is encapsulate said struct. Use that class as the base object, and add an operator so that it can be implicitly cast to a Rectangle. That way it can be passed to a method that needs a rectangle without casting.

    public class MyRect //class so you can inherit from it, but you could make your own struct as well
    {
        public int X { get; set; }
        public int Y { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public int Right { get { return X + Width; } }
        public int Bottom{ get { return Y + Height; } }

        public static implicit operator Rectangle(MyRect rect)
        {
            return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
        }

        public static implicit operator MyRect(Rectangle rect)
        {
            return new MyRect { X = rect.X, Y = rect.Y, Width = rect.Width, Height = rect.Height };
        }

    }       
 }

Now you can create your own rect manually or from an existing one:

  MyRect rect = ARectangleVar

And you can use it in legacy methods that expect a Rectangle without casting

Fare answered 16/6, 2012 at 17:56 Comment(5)
Curious, does either have any negative implications? And could MyRect inherit from something as well (or if it can is it not recommended)? Also thanks for including how to also make one from existing, didn't think of adding that and might come in handy :)Jago
Can't think of any implications that would cause real problems. The only noticeable one I can think of, is that by using a class, the original object is no longer immutable. If X is changed in a called method, the original object is changed. Of course that can be both an advantage as well as a disadvantage. There might be a slight overhead from the casting, but the optimization that can be created with your own class outweighs that imo. Inheriting MyRect itself from another class is no problem. Glad it was of help :)Fare
Thanks for more details ;) Finding out how to use them after you gave me the term wasn't to hard along with your examples, but finding why/when not to use can be harder (and often is in some really complicated document).Jago
@Kyomu - building on what Me.Name has said regarding extension methods, you can declare an Interface called, say, IRectangle with a single property Rectangle that returns a rectangle struct. You can make other structs that have a rectangle as one of their fields and have them implement IRectangle and have that single property. Then you can write a bunch of extension methods on IRectangle (ex: public static int GetSurface(this IRectangle rect, ...) and all those structs will get that behavior. Extension methods on interfaces can be very handy.Acalia
@hatchet Thanks, I probably won't use it in this case, but that's good to know as they could be quite handy as you say ;)Jago
C
4

Here's a possible workaround (put this in MyClass, the class you speak of that has a Rectangle property):

public static implicit operator Rectangle(MyClass obj)
{
    return obj.Rectangle;
}

This will, e.g. allow you to pass your object to a method expecting a Rectangle, and it work. Note that unlike with true inheritance, the Rectangle that exists isn't the same instance of MyClass that you passed in, it's just a struct made from the value.

Canner answered 16/6, 2012 at 17:55 Comment(0)
T
0

Value types are fundamentally incompatible with the style of inheritance that is applicable to class types, but it would be helpful to be able to declare a "derived struct" in such a way that the compiler would recognize a bidirectional identity-preserving conversion between the derived type and the original. The derived struct would be required to have exactly one field, called "Base" (though a C# compiler could accept the keyword base as a substitute), whose type was that of the struct being derived. All members of "Base" whose names did not match those of the surrounding type would be regarded as members of the enclosing structure.

The primary things a "derived struct" would offer, but which are presently lacking, would be:

  1. The ability to access members of the wrapped struct without having to add an extra layer of syntactic indirection. For example, if one had an instance `MyFancyPoint` of a type defived from `Point`, one could say `MyFancyPoint.X += 5` rather than `MyFancyPoint.Base.X += 5`.
  2. The ability to do more than one implicit or explicit type conversion at a time. C# generally generally only allows conversion from `X` to `Y` except when a direct conversion exists, in part because searching through all possible conversion sequences would be difficult, and in part because doing so would often yield ambiguities as to which sequence of conversions should be employed. If all types wrapping a particular struct are assumed to represent a "cloud" with mutal identity-preserving conversions, a conversion between two derived structs `XX:X` and `YY:Y` could be regarded unambiguously as a conversion to `X`, then `Y`, then `YY` (which would be legal iff `X` to `Y` conversion exists).
  3. The ability to regard a ByRef to an instance of one type as a ByRef to an instance of the other, and a ByRef to a field of one to be considered a ByRef of the corresponding field in the other.

I'm not sure any changes to the runtime would be required to support any of these features, if one accepts that an item of a boxed derived type may only be unboxed as that same derived type; allowing a boxed version of a struct to be cast directly to an instance of an identity-convertible derived one would be helpful, but might add complexity. I'm not sure if such behavior would be required to make generics work. Unfortunately, I get the feeling that many of the people responsible for making this sort of decision really don't like value types. Because of some design decisions early in the history of .net (especially the lack of "const ref" parameters and a means of exposing properties by ref), making structs work in semantically-correct fashion can be a challenge, and I think some of the implementers would rather use the problems with structs as an excuse not to improve .net's handling of them, than add features that would fix the problems.

Toh answered 17/6, 2012 at 17:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.