2015-12-23 28 views
1

我有代码结构:如何投放类型,它的通用基类型

SpecificType.cs

public class SpecificType : TypeBase<SpecificT, SpecificV> 
    where T : ITBase 
    where V : IVBase 
{ ... } 

SpecificT.cs

public class SpecificT : ITBase { ... } 

SpecificV.cs

public class SpecificV : IVBase { ... } 

TypeBase.cs

public class TypeBase<T, V> : IBase<T, V> 
    where T : ITBase 
    where V : IVBase 
{ ... } 

IBase.cs

public interface IBase<T, V> 
    where T : ITBase 
    where V : IVBase 
{ ... } 

所有我想要做的就是投我SpecificType到它的最抽象的类型 - IBase<T,V>

SpecificType specTypeObject = new SpecificType(); 
IBase<ITBase, IVBase> typeObject = (IBase<ITBase, IVBase>)specTypeObject; 

所有我收到的是InvalidCastException。这我甚至想实现吗?

+0

为什么你需要做显式强制转换,因为你到了基类?你如何在实现中使用类型属性?只在输入位置或输出位置? – neo

+1

[MSDN:泛型中的协变和反变量](https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx) – Dmitry

+0

您使用哪个.NET版本? – neo

回答

0

是的,但是您必须遍历该类型的层次结构堆栈以确定或构建执行铸造检查以确定您之后的代码。

+0

你能解释为什么我必须使用反射吗?我认为编译器很容易扩展继承层次结构。 – Fka

+1

@ Fk2阅读C#中的协变和逆变。至关重要的是,“列表”不是“列表”的基本类型。如果你可以将IBase的类型参数声明为协变,你就可以做你想做的事情,但是如果它们曾经在“输入”位置使用过,你将不能声明它们。 – phoog

+0

我错了,你必须使用'Type'并向上移动它的声明栈,我的答案指出你可以编写一些可以测试实例的东西,以便某些类型可以轻松确定基类。 –

3

问题是类型安全。假设我们有水果类和从其派生出的其他两类AppleCocunut。以下示例来自C#5.0 Unleashed book。

Apple[] apples = new Apple[] { apple1, apple2, apple3 }; 

// Because of array type covariance we can write the following. 
Fruit[] fruits = apples; 

// We're putting a Coconut, which is a Fruit, in a Fruit array. This is fine. 
fruits[2] = new Coconut(); 
// An element of an Apple[] should be an Apple, right? 
apples[2].Peel(); 

如示例所示,在使用输入类型参数时,类型安全性被破坏。由于椰子是水果,我们可以将它输入到苹果数组中,因为我们将苹果数组放到水果数组中。当使用水果数组参考时,我们能够在苹果中插入椰子。当我们在椰子上调用Peel方法时,由于椰子没有Peel方法,我们得到一个错误。这打破了类型安全。为了避免这种情况,类型参数的使用必须通过在T中说明out T来指示为输入或输出。如果你定义为out T,那么你可以使用T作为方法的返回值。如果您在T中定义为,那么您只能在输入位置使用T.这种方式确保了安全性。如果你需要将T用于输入和输出,那么你不能进行你想要的演员,因为它会打破类型安全。

3
SpecificType specTypeObject = new SpecificType(); 
IBase<ITBase, IVBase> typeObject = (IBase<ITBase, IVBase>)specTypeObject; 

因为SpecificTypeIBase<ITBase, IVBase>类型的不是你不能做这个转换。SpecificType居然有IBase<SpecificT, SpecificV基本类型,所以下面的工作:

SpecificType specTypeObject = new SpecificType();  
IBase<SpecificT, SpecificV> typeObject = (IBase<SpecificT, SpecificV>)specTypeObject; 

你可以添加协方差符来得到它的工作:

public interface IBase<out T, out V> 
    where T : ITBase 
    where V : IVBase 
    {} 

var specTypeObject = new SpecificType(); 
var typeObject = (IBase<ITBase, IVBase>)specTypeObject; 

现在这个工作。尽管如此,这可能会阻止您的IBase界面上的某些操作,例如在IBase内不允许使用像void Add(T t)这样的方法。

此外,为了完整起见,请注意不能将协变或逆变应用于泛型类,仅适用于接口和委托。所以下面的方法是行不通的:public class TypeBase<out T, out V> : IBase<T, V>

+0

感谢您的回答。不幸的是,我的一个操作是'无效插入(T型)',正如你所说,这是不可能的。 – Fka

+0

是的,在这种情况下,你可以投入的最不具体的类型(除了对象)是'IBase '。 – Erik

+0

是的,伙计们解释了我:)正如我在T McKeown回答下写的,我解决了删除通用接口的问题。 – Fka