为什么在这里需要显式强制转换

时间:2019-07-09 05:56:46

标签: 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; }
    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>()

可以很好地编译,但是在这里是不需要的。

4 个答案:

答案 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

不。如果知道AI<A>,并不意味着AT是相同的。 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");
}

我交换了AB,然后将B投射到A。强制转换无效。

答案 2 :(得分:2)

编译器知道A-> I<A>B-> I<B>之间存在类型转换,编译器也知道TI<T>之间存在类型转换A。但是缺少必要的链接-TA之间的直接转换。因此失败了。

如果我确定要创建的所有具体类型(例如Bclass)都是类,我将使用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的实例强制转换为父接口然后再转换为特定类型的原因。