簡體   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