Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

where clause on a constructor in C#?

Tags:

c#

generics

So here's what I'm trying to do.

I'm creating a generic class that allocates the type specified by the generic parameter in one of two ways, determined by which overloaded constructor is used.

Here is the example:

class MyClass<T>
    where T : class
{
    public delegate T Allocator();
    public MyClass()
    {
        obj = new T();
    }

    public MyClass( Allocator alloc )
    {
        obj = alloc();
    }

    T obj;
}

This class requires that type T is a reftype in all cases. For the default constructor, we want to instantiate T via its default constructor. I'd like to put a where T : new() on my default constructor, like this:

public MyClass()
    where T : new()
{
    obj = new T();
}

However, this is not valid C#. Basically I only want to add the constraint on type T to have a default constructor only when the default constructor of MyClass() is used.

In the second constructor for MyClass, we let the user determine how to allocate for T with their own allocation method, so obviously it makes sense for MyClass to not enforce T be default constructible in all cases.

I have a feeling that I'll need to use reflection in the default constructor for this, but I hope not.

I know this can be done because the Lazy<T> class in .NET 4.0 does not require T to be default constructible at the class level, yet it has constructors similar to those in my example. I'd like to know how Lazy<T> does it at least.

like image 663
void.pointer Avatar asked May 12 '11 13:05

void.pointer


2 Answers

You can only include constraints in the declaration where you're introducing a generic type parameter.

However, you could introduce a generic method on a non-generic type:

public class MyClass
{
    public static MyClass<T> Create<T>() where T : class, new()
    {
        return new MyClass<T>(() => new T());
    }
}

public class MyClass<T> where T : class
{
    T obj;

    public MyClass(Allocator allocator)
    {
        obj = allocator();
    }
}

(I'd personally just use Func<T> instead of declaring a separate delegate type btw.)

Then you can use:

MyClass<Foo> foo = MyClass.Create<Foo>(); // Enforces the constraint
like image 110
Jon Skeet Avatar answered Oct 08 '22 01:10

Jon Skeet


Basically I only want to add the constraint on type T to have a default constructor only when the default constructor of MyClass() is used.

It is not possible to enforce this with a constraint on T. You either have it be the case that where T : new() is specified in the definition of MyClass<T> or you do without such a constraint at all.

I have a feeling that I'll need to use reflection in the default constructor for this, but I hope not.

You can't enforce the constraint, but you can say

obj = Activator.CreateInstance<T>();

which will invoke the default constructor for T.

I know this can be done because the Lazy class in .NET 4.0 does not require T to be default constructible at the class level, yet it has constructors similar to those in my example. I'd like to know how Lazy does it at least.

Lazy<T> requires you specify a delegate that returns instances T. Basically, it requires you to specify a Func<T>.

like image 32
jason Avatar answered Oct 08 '22 00:10

jason