繁体   English   中英

我如何获得通用接口的接口实现

[英]how can i get the interface implementation of a generic interface

我有一个具有以下类/接口结构的类:

public class Foo : IBar<FooBar>, INonGenericBar
{
    // class implementation
     public override IsGeneric => return true;
     public override IsNonGeneric => return true;
}

public IBar<T>
{
    bool isGeneric {get;}
}

public INonGenericBar
{
    bool IsNonGeneric {get;}
}

我知道可以使用isas关键字获得 INonGenericBar 的实现;

var myFoo = new Foo();
var barIsNonGeneric = myFoo is INonGenericBar;
Assert.IsTrue(barIsNonGeneric);
var nonGenericBar = myFoo as INonGeneric;
Assert.IsTrue(nonGenericBar.IsNonGeneric);

但由于 IBar 是一个通用接口,看来我需要知道确切的通用实现

var myFoo = new Foo();
var isGenericBar = myFoo is IBar<FooBar>
var isParentGenericBar = myFoo is IBar<object>

Assert.IsTrue(isGenericBar);
Assert.IsFalse(isParentGenericBar);

var GenericBar = myFoo as IBar<FooBar>;
var GenericParentBar = myFoo as IBar<object>;

Assert.IsNotNull(GenericBar);
Assert.IsNotNull(GenericParentBar); // fails

然而,我只对 IBar<> 接口本身感兴趣,而不是它使用的通用类型。 有没有办法让我可以将它投射到 IBar<>?

C# 不支持“泛型类型,但我不关心类型参数”的概念 - 这就是为什么许多具有泛型类的库将其所有非泛型成员放在非泛型超类中(或泛型接口放入它们在非通用父接口中的非通用成员)。 1

(奇怪的是, Java确实支持这种情况,但(具有讽刺意味的是)这只是因为 Java 的泛型是使用类型擦除而不是 .NET 的具体泛型1 实现的

也就是说,C#确实允许您使用typeof运算符获得泛型类型的“开放类型” - 但仅限于运行时反射的上下文 - 因此您不能在正常程序代码中使用它。

所以这没问题:

Type openType = typeof(IBar<>);

但这不是:

IBar<> anyIBar = ...

C# 中唯一的解决方案是将IBar所有不依赖于T的成员重构移动到INonGenericBar

根据我对 .NET CLR 的理解:如果任何其他 CLR 语言想要支持此功能,他们也必须通过某种反射 hack 来实现这一点,因为 CLR 本身不支持对没有类型参数的泛型对象的运行时引用(我可能是错的!)

更新!

显然,微软知道对“通配符泛型”的支持请求,这个 GitHub 问题正在跟踪任务 - 但不要指望这会实施多年- 考虑到它花了54 年从 Tony Hoare 1965 年发明的空引用到C# 8.0 的非空引用类型的实现)所以不要屏住呼吸。


1这绝对是 C# 语言和/或 .NET CLR 的设计错误,因为它有效地破坏了泛型继承(即每个泛型类型Dog<T>可能需要一个非泛型父Dog ,但如果您有一个泛型子SamoyedDog<T> ,那么它也可能需要一个非泛型的父SamoyedDog ,但 C# 和 .NET CLR 将不允许SamoyedDog<T>SamoyedDogDog<T>派生。

2我认为这具有讽刺意味 [3] 因为具体化泛型的全部意义在于拥有“一流”的泛型,而不是只在编译时重要的 Java 的二等泛型——具有讽刺意味的是,Java 实现的最终结果使(至少在这种情况下)更大的灵活性,这是一流的具体泛型的预期结果。 我注意到(假设这是 CLR 限制).NET CLR 和 C#可以直接支持无参数泛型,但需求并不存在,因为替代方案(即重构)要简单得多,而 C#/ .NET CLR 团队决定将精力花在其他新功能上。

[3] 作为一个英国人而非美国人,我为正确使用讽刺而感到自豪!

如果我理解正确,您需要使IBar<T>协变。 之后,您可以在不获得空值的情况下执行此操作:

var GenericParentBar = myFoo as IBar<object>;

要使IBar<T>协变,只需在泛型参数中添加out修饰符;

public IBar<out T>
{
    bool isGeneric {get;}
}

请注意,在此更改之后,您无法向IBar添加任何将T作为参数的方法。 您只能添加返回T方法。

要了解有关方差的更多信息,请转到此处

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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