C# 编译器无法转换受约束的泛型类型
我有一个类的泛型类型为“G” 在我的班级模式中,我有C# 编译器无法转换受约束的泛型类型,c#,generics,casting,constraints,C#,Generics,Casting,Constraints,我有一个类的泛型类型为“G” 在我的班级模式中,我有 public class DetailElement : ElementDefinition 假设我有一个这样的方法 public void DoSomething<G>(G generic) where G : ElementDefinition { if (generic is DetailElement) {
public class DetailElement : ElementDefinition
假设我有一个这样的方法
public void DoSomething<G>(G generic)
where G : ElementDefinition
{
if (generic is DetailElement)
{
((DetailElement)generic).DescEN = "Hello people"; //line 1
//////
ElementDefinition element = generic;
((DetailElement)element).DescEN = "Hello again"; //line 3
//////
(generic as DetailElement).DescEN = "Howdy"; //line 5
}
else
{
//do other stuff
}
}
但是第三行很好用。
我可以通过执行第5行中编写的代码来解决这个问题
我想知道的是,为什么编译器报告第1行的错误而不是第3行的错误,因为据我所知,它们是相同的。
编辑:我担心我可能遗漏了框架逻辑的一些重要部分
edit2:虽然编译器错误的解决方案很重要,但我的问题是为什么编译器在第1行报告错误而不是在第3行报告错误。where子句不应该是“where G:DetailElement” 在您编写的代码中,DetailElement是ElementDefinition,但ElementDefinition不一定是DetailElement。因此隐式转换是非法的 是否有其他类型的ElementDefinition可以传递到此方法中?如果是这样,当您尝试将它们转换为DetailElement实例时,它们将抛出异常 编辑: 好的,现在您已经更改了代码列表,我可以看到您正在检查类型,以确保在输入代码块之前它确实是DetailElement。不幸的是,事实是,即使您自己已经检查了类型,也不能隐式地向下转换。我认为你真的应该在块的开头使用“as”关键字:
DetailElement detail = generic as DetailElement;
if (detail == null) {
// process other types of ElementDefinition
} else {
// process DetailElement objects
}
更好的是,为什么不使用多态性来允许每种元素定义定义自己的DoSomething方法,并让CLR为您负责类型检查和方法调用呢?如果
G
被限制为DetailElement
(其中G:DetailElement
)然后您可以继续并将G
转换为ElementDefinition,即“(ElementDefinition)generic
”。但是,因为在运行时,G
可能是ElementDefinition
的另一个子类,而不是DetailElement
,所以在类型未知且无法验证的编译时,它将不允许它
在第3行中,您从中强制转换的类型是已知的元素定义
,因此您所做的只是向上强制转换。编译器不知道它在运行时是否会成功转换,但它会信任您。编译器不太信任泛型
第5行中的as
运算符也可能返回null,并且编译器不会静态检查类型,以确定在这种情况下是否安全。您可以将作为
用于任何类型,而不仅仅是与元素定义
兼容的类型
从MSDN开始:
编译器只允许您将泛型类型参数隐式转换为对象或约束指定的类型
这种隐式转换当然是类型安全的,因为任何不兼容都是在编译时发现的
编译器将允许您显式地将泛型类型参数强制转换为任何接口,而不是类:
interface ISomeInterface {...}
class SomeClass {...}
class MyClass<T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
}
}
如果您有很多担心的元素定义,那么这将导致更多的代码,但可能是您将得到的最精巧的、不涉及的代码,这将被视为胡说八道
public void DoSomething<G>(G generic)
where G : ElementDefinition
{
DetailElement detail = generic as DetailElement;
if (detail != null)
{
detail.DescEN = "Hello people";
}
else
{
//do other stuff
}
}
它是有效的,但as形式可能是最好的 通常,向上投射是一种代码气味。可以通过方法重载来避免这种情况。试试这个:
public void DoSomething(DetailElement detailElement)
{
// do DetailElement specific stuff
}
public void DoSomething<G>(G elementDefinition)
where G : ElementDefinition
{
// do generic ElementDefinition stuff
}
为了简单起见,我没有把所有的代码都放在这里。我会把它编辑成正确的
public void SomeMethod(T t)
{
if(t is int) {...}
string str = t as string;
if(str != null) {...}
}
public void DoSomething<G>(G generic)
where G : ElementDefinition
{
DetailElement detail = generic as DetailElement;
if (detail != null)
{
detail.DescEN = "Hello people";
}
else
{
//do other stuff
}
}
DetailElement detail = (DetailElement)(object)generic;
public void DoSomething(DetailElement detailElement)
{
// do DetailElement specific stuff
}
public void DoSomething<G>(G elementDefinition)
where G : ElementDefinition
{
// do generic ElementDefinition stuff
}
DetailElement foo = new DetailElement();
DoSomething(foo); // calls the non-generic method
DoSomething((ElementDefinition) foo); // calls the generic method