C# Feature Request: Type Member Level Generic Constraints

The Basic Need

Take the following code:

public abstract class Base
{
protected Base(int something)
{
// ...
}
}
public class Derived : Base
{
public Derived(int something)
: base(something)
{
// ...
}
}
public class Derived2 : Base
{
public Derived2(int something)
: base(something)
{
// ...
}
}

Where Base is not my code, forcing me to create non-empty constructors so I could call the base constructor.
Now, I would like to accept types deriving from Base as a generic parameter and instantiate them in my type. This is how my could will have to look:

public class Generic<TBase> where TBase : Base
{
public void Method()
{
Base something = (Base)Activator.CreateInstance(typeof(TBase), 0);
}
}

Argh! I have to use Activator.CreateInstance, which is both slow and untyped. I can’t move the initialization from the constructor to another method, since it’s not up to me. What a fall from grace. What I need here is something similar to duck typing.
So here’s a syntax I’m suggesting for the next version of C#:

public class Generic<TBase> where TBase : Base, new(int)
{
public void Method()
{
Base something = new TBase(0);
}
}

Now there’s no messy untyped stuff in my code, plus I get Generics’ performance boost.

The Next Level

Why stop at constructors? Let’s change our code a bit:

public class Derived : Base
{
public Derived(int something)
: base(something)
{
// ...
}
public int Foo()
{
}
}

Say I have another class, DerivedEx, which exists in an assembly I have no control over, derives from Base and has the same signature method Foo.
Now I want to limit my types to only those that have this Foo method. I can put an interface on Derived, but since I can’t control DerivedEx, I can’t place an interface on it.
Why not use what we’ve already started then:

public class Generic<TBase> where TBase : Base, has Func<int> Foo, new(int)
{
public void Method()
{
TBase something = new TBase(0);
int value = something.Foo(); } }

Notice that we are, in effect, staying completely typed: The compiler is certain that any type argument supplied to Generic has an integer accepting constructor and a function named Foo which adheres to the Func<int> delegate. On the other side, the compiler can check the supplied type argument for these conditions.

I’ve taken great care to make sure the syntax I propose does not conflict with C# 3.0’s, but I wouldn’t mind it being changed – just that it gets implemented. ;)

Advertisements

18 thoughts on “C# Feature Request: Type Member Level Generic Constraints

  1. Seems like the “has Func” syntax isn’t completely necessary. If what you want is a specific function to exist just specify an interface, such as:
    public class Generic where T : Base, IExample { }
    and IExample has your method signature. Or even simpler:
    public class Generic where T : Derived { }
    Why bother with the base class you have no control over if you’re just going to be inheriting from it anyway?

  2. Meile,
    I will, thanks :)
    Darren,
    No, that only works for parameterless constructors.
    Justin,
    I forgot to mention that DerivedEx was a third-party class. I’ll fix the post. Thanks. :)

  3. You can do your something very similar to your Func request with Scalas views (http://www.scala-lang.org/intro/views.html). I think any C# solution to this “static duck typing” style of thing will involve some sort of interface (i.e. your Func Foo becomes an interface with that method).
    Totally agree with you on the constructor constraints though, it’s pretty obvious they had that extension in mind when they came up with the syntax.
    I’ve seen Ayende Rahein (http://www.ayende.com/Blog/) fix this problem by using something like:
    public class Generic where TBase : Base, TBaseFactory : IBaseFactory, new()
    {
    public void Method()
    {
    Base something = TBaseFactory().CreateBase(0);
    }
    }
    This might be somewhat nicer if we could have interfaces with static members on them though!

  4. In keeping with: http://blogs.msdn.com/charlie/archive/2008/01/25/future-focus.aspx
    Why not:
    public dynamic interface IFoo {
        int Foo();
    }
    public class Generic where TBase:Base, IFoo {
        IBaseFactory _baseCreator;
        public Generic(IBaseFactory baseCreator) {
            _baseCreator = baseCreator);
        }
        public void Method() {
            Base something = _baseCreator.CreateBase(0);
        }
    }
    or put the dynamic on the constraint:
    where TBase:Base, dynamic IFoo …
    I don’t know which would be better, but I would see either of them more likely for inclusion than your “has Func” syntax (which is a bit complex for anything more than a simple function).

  5. well, that didn’t look right, lets try again…
    public dynamic interface IFoo {
    int Foo();
    }
    public class Generic where TBase:Base, IFoo {
    IBaseFactory _baseCreator;
    public Generic(IBaseFactory baseCreator) {
    _baseCreator = baseCreator);
    }
    public void Method() {
    Base something = _baseCreator.CreateBase(0);
    }
    }

  6. Many people have been suggesting this, but the idea shows a basic misunderstanding of constructors. Constructors allow a class to specify the data that it needs to be constructed; but there is no reason why the constructor for a subclass has to match the constructor for a base class. In your example, the following class could not be used in your Generic class:
    public class Derived3 : Base
    {
    public Derived3(string somethingElse)
    : base(3)
    {
    // …
    }
    }
    A much better option is for the Generic class to take a delegate which can create an instance of the desired type:
    public class Generic2 where TBase : Base
    {
    private readonly Func _create;
    public Generic2(Func create)
    {
    _create = create;
    }
    public void Method()
    {
    TBase something = _create(0);
    int value = something.Foo();
    }
    }
    public static class Program
    {
    public static void Main()
    {
    Generic2 d = new Generic2(i => new Derived(i));
    Generic2 d2 = new Generic2(i => new Derived2(i));
    Generic2 d3 = new Generic2(i => new Derived3(i.ToString());
    }
    }

  7. You may have missed my idea. I was talking about using constructors as part of the contract for a generic type argument, since they can’t be expressed in any other way, such as interfaces, inheritance, etc.
    It is true that a constructor is special in the way that it is used per-type as it sees fit, but sometimes a design has types forced to write a certain constructor, like ISerializable.
    Using your idea is nice, if want to allow an untyped two-pulse construction – after all, there’s no way to know whether the type returned from the lambda is actually the one the class needs and also that the value of i itself isn’t specified until a call to a different method, which, if you look at it this way, means the class is only halfway constructed.

  8. “…there’s no way to know whether the type returned from the lambda is actually the one the class needs…”
    “…the class is only halfway constructed…”
    I don’t understand. Lambdas are fully typed; there is no way in my example for an instance of the “wrong” type to be returned. And in what way is the returned class “halfway constructed”? It looks fully constructed to me…

  9. The first comment I made was regarding the fact that I could use a class derived from DerivedX. Not that big of a deal, but it might be to some.
    The second comment is about the fact that the class needs the integer value and not the DerivedX class. You’re only instantiating the integer value after the construction of the class, which means the class isn’t fully constructed (that is, doesn’t have all the data it needs to operate) until the second call.

  10. Accepting derived classes is the whole point. If your solution can’t accept an instance of a derived class, there is something wrong with your class hierarchy.
    I still don’t understand your second point. My derived class is using chained constructors to pass an integer to its base class. In chained constructors, the base class constructor runs first. So I don’t see how you can end up with an object that “isn’t fully constructed.”

  11. Never mind these things – we’ve gone pretty far from the original point.
    Let’s go back to basics – I fail to understand how your example solves the problem I presented.

  12. The problem you presented was how to construct an instance of a generic type from within a generic type without generic constructor constraints. The solution I presented works in the current version of C#, it works for types which have alternate constructors, and doesn’t require duck typing.

  13. This is silly:
    public class Generic where TBase : Base, has Func Foo, new(int)
    – If you want Generic to have a certain method or property, in this case (Func), it should be defined on Base
    – Limiting the constructors:
    Generic, new(int), new(int,object),new(int,object,string)
    This is completely meaningless, further, the creator of Generic cannot possibly know how it will be derived

  14. John,
    1. What if I don’t have access to Base nor to any of its derived types?
    2. It’s a missing feature from interfaces that many have long since asked for and has turned into a problem for generic inheritance. It’s not as meaningful as it would be defined on an interface, but still.
    I fail to understand your last sentence: “the creator of Generic cannot possibly know how it will be derived”…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s