简体   繁体   中英

Problems with abstract types and interafaces as generic type params with the new() constraint

I need to have the generic type parameter as an interface, however I would like to instantiate the type in the generic class (SomeGenericType) as follows:

class Program
{
    static void Main(string[] args)
    {
        var val = new SomeGenericType<ISomeInterface>();

        Console.ReadKey();
    }
}

internal class SomeGenericType<T> where T : new()
{
    public SomeGenericType()
    {
        var test = new T();  
    }
}

public class SomeClass : ISomeInterface
{
    public string TestVal { get; set; }
}

public interface ISomeInterface
{
    string TestVal { get; set; }
}

This throws the following compile time error:

"ISomeInterface must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method SomeGenericType"

I understand why it happens, however I was wondering if there is any way around this problem?

Thanks.

No, the new() constraint requires that an instance of the type can be created with the syntax

new T()

This clearly isn't true of either an abstract class or an interface, only a concrete class with a public parameterless constructor.

You could defer the problem until runtime by removing the constraint, and using:

Activator.CreateInstance<T>()

instead to create the object. Then as long as the actual type used at runtime satisfies these constraints, your code will work as you want it to. However, if you do attempt to use an interface or an abstract class, then you will encounter a runtime error.

In your specific case, this line would throw an exception

var val = Activator.CreateInstance<SomeGenericType<ISomeInterface>>();

You're past the compile-time error, but to no effect.

An alternative idea, which may be irrelevant, but it looks like you are looking for a way to ask for an ISomeInterface , and have an instance of its "default" implementation SomeClass provided. This is the sort of thing that an Inversion of Control (IOC) container can handle for you. If you want to investigate further, you could look at Spring.NET, Microsoft Unity, AutoFac, LinFu or one of many other frameworks.

The issue here is the new constraint is tied to having a concrete type implementation. This can't ever work with simply and interface or abstract class since they cannot be directly instantiated. You must provide a concrete class here

var val = new SomeGenericType<SomeClass>()

The problem is, there is no way for the compiler to know which class to instantiate for the given interface. As David M points out:

This is the sort of thing that an Inversion of Control (IOC) container can handle for you

I think using a framework might be over kill for this simple requirement. What you can do is create a Factory class of your own like this:

public class Factory
{
  Dictionary<Type, Type> typeMapping = new Dictionary<Type, Type>();

  public void Register<IType, CType>()
  {
    typeMapping.Add(typeof(IType),typeof(CType));
  }

  public IType Create<IType>()
  {
    Activator.CreateInstance(typeMapping[typeof(IType)]);
  }
}

throw in a few sanity checks and this class should be ready to use.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM