我正在使用与此类似的工厂:
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();
我不完全理解的是:为什么要双重铸造
(T)(I<T>)new A()
必要吗?编译器知道
new A("A") is I<A>
实际上知道
new A("A") is T
顺便说一句:我正在使用通用接口,否则
Create<I>()
可以很好地编译,但是在这里是不需要的。
答案 0 :(得分:2)
您的代码说明,当typeof(T) == typeof(A)
为true
时,它保证表达式new A(...)
的类型与T
兼容。
尽管这种推理在运行时是正确的,但是C#编译器不会将typeof(T) == typeof(A)
视为任何类型的“类型保护”。
编译器知道
new A("A")
是I<A>
同意
并且实际上知道
new A("A")
是T
不,它不能。在基于模板的方法中(例如在C ++中),在源代码中遇到的每个T
都会编译“泛型”函数,然后编译器就会知道。但是,在.NET中,泛型是基于运行时的,因此C#编译器必须编译适用于符合约束条件的任何T
的代码,但除此之外,具体的T
在编译时是未知的
如果要使用编译器“类型防护”并避免显式强制转换,则可以使用C# pattern matching重写代码:
public T Create<T>()
where T : I<T>
{
if (typeof(T) == typeof(A) && new A("A") is T retA)
return retA;
else if (typeof(T) == typeof(B) && new B(2) is T retB)
return retB;
else
throw new Exception("Unknown className");
}
答案 1 :(得分:2)
实际上知道
new A("A") is T
不。如果知道A
是I<A>
,并不意味着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");
}
我交换了A
和B
,然后将B
投射到A
。强制转换无效。
答案 2 :(得分:2)
编译器知道A
-> I<A>
和B
-> I<B>
之间存在类型转换,编译器也知道T
和I<T>
之间存在类型转换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");
}
类型约束。它使我们能够简化代码,并进行一些重构,使自己的代码可能类似于:
buildPanel(index, item) {
let panel = [];
let keys = DBkeys['Requests'].MyRequest;
let status = item[keys['status']];
panel.push(<View style={{ position: 'absolute', right: 0, bottom: 0, padding: normalize(5), alignItems: 'center' }} key={'status'}>
<TouchableOpacity onPress={this.handleShowModal()}>
<Icon name={img.itemStatus[status].name} type={img.itemStatus[status].type} color={img.itemStatus[status].color} size={normalize(38)} />
</TouchableOpacity>
</View>);
return panel;
}
<View style={[styles.panelContainer, status === 'success' ? {} : { backgroundColor: color.white }]}>
<FlatList
showsVerticalScrollIndicator={false}
progressViewOffset={-10}
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
onMomentumScrollEnd={(event) => event.nativeEvent.contentOffset.y === 0 ? this.onRefresh() : null}
data={content}
renderItem={({ item }) => item}
keyExtractor={(item, key) => key.toString()}
/>
</View>
<IconModal visible={this.state.modalVisible} close={this.handleDismissModal} status='test' desc='test' />
答案 3 :(得分:2)
我不完全理解的是:为什么要双重铸造 必要?编译器知道
编译器知道以下内容
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>
这就是为什么必须先将T的实例强制转换为父接口然后再转换为特定类型的原因。