C# 为什么这里需要显式强制转换
我使用的工厂与此类似:C# 为什么这里需要显式强制转换,c#,C#,我使用的工厂与此类似: interface I<T> { void Print(); } class A : I<A> { public string Id { get; } public A(string id) { Id = id; } public void Print() { Console.WriteLine(Id); } } class B : I<B> { public int Id { get; }
interface I<T>
{
void Print();
}
class A : I<A>
{
public string Id { get; }
public A(string id) { Id = id; }
public void Print() { Console.WriteLine(Id); }
}
class B : I<B>
{
public int Id { get; }
public B(int id) { Id = id; }
public void Print() { Console.WriteLine(Id); }
}
class Factory
{
public T Create<T>()
where T : I<T>
{
if (typeof(T) == typeof(A))
return (T)(I<T>)new A("A");
else if (typeof(T) == typeof(B))
return (T)(I<T>)new B(2);
else
throw new Exception("Unknown className");
}
}
var sut = new Factory();
sut.Create<A>().Print();
sut.Create<B>().Print();
顺便说一句:我使用的是通用接口,因为不是这样
Create<I>()
Create()
编译很好,但这里不需要。您的代码认为当
typeof(T)==typeof(A)
为true
时,它保证表达式的类型新的A(…)
与T
兼容
尽管这种推理在运行时是正确的,C#编译器并没有将typeof(t)==typeof(A)
视为任何类型的“类型保护”
编译器知道新的A(“A”)
是I:
public T Create()
T:我在哪里
{
如果(类型(T)=类型(A)和新A(“A”)为T retA)
返回retA;
如果(类型(T)=类型(B)和新B(2)为T retB)
返回retB;
其他的
抛出新异常(“未知类名”);
}
事实上,我知道
new A("A") is I<A>
new A("A") is T
新的A(“A”)是T
否。如果它知道A
是I
,并不意味着A
和T
是相同的T
可能是B
,您的强制转换将失败。请看以下代码:
public T Create<T>()
where T : I<T>
{
if (typeof(T) == typeof(A))
return (T)(I<T>)new B(2);
else if (typeof(T) == typeof(B))
return (T)(I<T>)new A("A");
else
throw new Exception("Unknown className");
}
public T Create()
T:我在哪里
{
如果(类型(T)=类型(A))
报税表(T)(I)新的B(2);
否则如果(类型(T)=类型(B))
申报表(T)(I)新的A(“A”);
其他的
抛出新异常(“未知类名”);
}
我交换了A
和B
,而你将B
转换为A
。强制转换无效。编译器知道A
->I
和B
->I
之间存在类型转换,它还知道T
和I
之间存在转换。但是缺少必要的链接,A
和T
之间的直接转换。所以它失败了
如果我确实知道我要创建的所有具体类型,如A
和B
都是类,我会使用class
类型约束。它允许我们简化我们的代码,通过一些重构允许我自己,代码可能是以下内容:
public T Create<T>() where T : class, I<T>
{
if (typeof(T) == typeof(A))
{
return new A("A") as T;
}
if (typeof(T) == typeof(B))
{
return new B(2) as T;
}
throw new Exception("Unknown className");
}
public T Create(),其中T:class,I
{
如果(类型(T)=类型(A))
{
将新的A(“A”)返回为T;
}
如果(类型(T)=类型(B))
{
返回新的B(2)作为T;
}
抛出新异常(“未知类名”);
}
我不完全理解的是:为什么是双重演员
必要吗?编译器知道这一点
new A("A") is I<A>
new A("A") is T
编译器知道的是
A is I<A>
B is I<B>
T is I<T>
T is not A
T is not I<A>
T is not B
T is not I<B>
A是I
B是我吗
是我吗
T不是一个
不是我
T不是B
不是我
这就是为什么必须先将T的实例转换为父接口,然后再转换为特定类型。这可能是stackoverflow.com/questions/11555729/的直接副本……简言之,这里有一些气味,编译器的错误是静态分析,试图避免您犯运行时错误。双重强制转换意味着所有赌注都没有了,您已经失去了类型安全性,在泛型方法中使用typeof(T)
通常(不总是),但通常是重新思考问题的时候。我同意一般的说法。您最好将此方法拆分为CreateA
,CreateB
等。无论如何,您将为新类型添加新条件或新方法,但使用方法的方法没有此问题,因此它将是正确的方法。如果你没有在I
界面中使用t
,你为什么需要它呢?@将军我同意通用的创建方法很有趣,人们应该对每种类型使用专用的工厂方法,但真正的代码使用Create方法将输入对象映射到不同类型的结果,而在通用方法中这样做是重用我迄今为止找到的映射代码的唯一方法。这将给您一个令人不安的“CS8603:可能的空引用返回”。我认为这是迄今为止最好的解释(并且已经有一些很好的解释)再加上一个很好的重构(我必须仔细阅读才能理解),这说明了我的问题的运行时/编译时问题