简体   繁体   中英

Generic method returning objects of generic derrived types

I am struggling with generics in C# a little bit. Consider this piece of code:

public interface A {};
public interface B : A {};
public interface C : A {};

public interface IMyInterface<in T> where T : A {};

public class FirstImpl<T> : IMyInterface<T> where T : B {};
public class SecondImpl<T> : IMyInterface<T> where T : C {};

now I need to some factory, which takes a type and returns the correct implementation:

public IMyInterface<T> Create<T>() where T : A 
{
    if(typeof(T) is B) {
        return new FirstImpl<T>();
    }
    if(typeof(T) is C) {
        return new SecondImpl<T>();
    }
}

this doesn't work on two levels. the first one is return type. The second one is that I can't pass "T" into first or second implementation, since they require more specific type. Any idea how to solve this?

Any time you have code using a generic type or method, and that code does some sort of test on the generic type parameter, you're doing it wrong. (And IMHO it goes without saying that dropping into reflection is even worse.) The whole point of a generic type is to write code that doesn't depend on the generic type.

In your example, a better approach would be something like this:

static IMyInterface<T> CreateB<T>() where T : B
{
    return new FirstImpl<T>();
}

static IMyInterface<T> CreateC<T>() where T : C
{
    return new SecondImpl<T>();
}

Ie write different methods to handle each scenario. The caller is going to have to know what it's dealing with already anyway, if a constraint-based approach was going to work. So it's not a problem to just have different method names, each to be used in the appropriate and respective situation.

If the above does not address your specific scenario, please improve the question so that it includes a good Minimal, Complete, and Verifiable code example that shows not only the generic types you are trying to use, but also the context in which they will be used.

If you don't mind reflection, you could do it that way:

public static IMyInterface<T> Create<T>() where T : A 
{
    if (typeof(B).IsAssignableFrom(typeof(T)))
    {
        var type = typeof(FirstImpl<>);
        var boundType = type.MakeGenericType(typeof(T));
        return (IMyInterface<T>) Activator.CreateInstance(boundType);
    }
    else if(typeof(C).IsAssignableFrom(typeof(T)))
    {
        var type = typeof(SecondImpl<>);
        var boundType = type.MakeGenericType(typeof(T));
        return (IMyInterface<T>) Activator.CreateInstance(boundType);
    }

    throw new ArgumentException("unknown type " + typeof(T).Name);
}

You can try it in a .net fiddle

The shortest way would be to just add the extra constraints with the Create<T>() method like public IMyInterface<T> Create<T>() where T : A , B, C if you want to keep the design intact. You'd probably need one implementation for the default case to make the method complete.

public IMyInterface<T> Create<T>() where T : A, B, C
{
    if (typeof(T) is B)
    {
        return new FirstImpl<T>();
    }
    if (typeof(T) is C)
    {
        return new SecondImpl<T>();
    }
    return new DefaultImpl<T>;
}

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