C#从非通用父接口调用泛型接口方法而不使用动态

时间:2016-08-08 21:03:22

标签: c# .net generics dynamic

假设我们有一个非通用的基接口,带有一个通用的继承接口:

public interface IFoo { }
public interface IBar<T, K> : IFoo { 
    K Do(T t);
}

public class BarImpl : IBar<Type, AnotherType> {
    public AnotherType Do(Type type) {
        return new AnotherType(type);
    }
}

我需要创建一个返回IFoo实例的工厂,但是使用返回的实例我需要能够调用派生类型Do(T),这是不可用的。

public class FooFactory() {
    IFoo Get() {
           // simplified, in reality i am returning the correct
           // type by checking the generic interface
           // types to get an object from a stored list 
           // of implementations
           return BarImpl();
    }
}

// Now in another class
public void DoFoo() {
    IFoo iFoo = new FooFactory().Get();
    // Need to be able to call iFoo.Do(Type) but cannot
}

我能够让它工作的唯一方法是创建一个动态对象,而不是IFoo,然后调用Do() - 这在我的情况下有效,但我失去了一些类型安全,我更喜欢保持。

我的问题是我可以重新设计它以便能够访问派生接口方法,同时仍然能够维护IFoo的列表(以及随后的工厂方法返回类型)

2 个答案:

答案 0 :(得分:3)

您期望或希望类型安全,但请以这种方式考虑:

  • 为了能够调用DoGet需要返回定义该方法的类型。 IFoo没有它,但IBar<T, K>却有。 Get会返回一个IFoo对象,但不保证是IBar<T, K>
  • 即使Get的实现确保只返回IBar<T, K>,类型系统也无法知道此而不实际返回该类型
  • 假设您可以返回允许您调用Do方法的类型,则类型将不清楚:您需要将类型T的对象传递给它。但是,返回的IFooIBar<T, K>不一定使用您要传递给T的相同类型Do
  • 即使Get的实施会提供此功能(例如“让我IBar<T, K>接受类型T”),类型系统也有办法反映这一点,那么这仍然不会说K。对于已知的T,它可能仍然是IBar<T, int>IBar<T, string>。如果没有实际的具体类型,实际上没有办法知道。
  • 并且,假设这适用于类型系统:这实际上有什么用途?您可以使用正确的类型参数调用泛型类型的方法:但返回值仍然没有具体类型。你无法对Do的返回类型说些什么。

我的观点是,当您确实有理由维护具体类型时,您只需要泛型类型。通常,如果从另一个非泛型方法调用泛型方法或泛型类型的方法,那么您要么使用一组离散的类型,要么实际上不需要泛型类型信息。

所以也许你最好在这里引入非通用IBar类型:

interface IBar
{
    object Do(object t);
}
interface IBar<T, K> : IBar
{
    K Do(T t);
}

public class BarImpl : IBar<Type, AnotherType>
{
    public AnotherType Do(Type type)
    {
        return new AnotherType(type);
    }

    public object Do(object t)
    {
        return Do((Type) t);
    }
}

然后,您可以让Get返回IBar,然后您可以拨打Do

顺便说一下。这种模式在BCL中非常常用,例如: IEnumerable<T>IEnumerable

答案 1 :(得分:0)

您可以向object DoIt()添加IFoo方法,然后BarImpl将实施该方法:

public object DoIt()
{
    return Do(typeof(T));
}

这里的问题是你需要将DoIt的返回值转换为实际类型。但是你不会知道先前是什么。