8

我对以下代码有一些问题。我想为一个对象显式一个字符串,这工作得很好,但是,如果这个对象是一个泛型类的一部分,这将失败并出现以下错误异常:“无法转换类型为'System.String'的对象输入'test.B'”。即使我已经重载了该方法。

using System;
using System.Collections.Generic;

namespace test {
    class Program {
        static void Main(string [] args) {
            // These two cast perfectly fine.
            B x = (B) "abc";
            C y = (C) "def";

            A <B> a = new A<B>();
            a.b();
            A <C> b = new A<C>();
            b.b();
        }
    }

    class A<T> {
        public List <T> a = new List<T>();

        public void b() {
            // Unable to cast object of type 'System.String' to type 'test.B'
            this.a.Add ((T) (object) "abc"); 
            this.a.Add ((T) (object) "def");
            this.a.Add ((T) (object) "ghi");
        }
    }

    class B {
        public string b;

        public static explicit operator B(string a) {
            B x = new B();
            x.b = a;
            return x;
        }
    }

    class C {
        public string c;

        public static explicit operator C(string a) {
            C x = new C();
            x.c = a;
            return x;
        }
    }
}

如果有人能向我解释为什么这不是正确的投射,那就太棒了。

谢谢

4

4 回答 4

12

转换运算符仅适用于静态已知类型的情况;毕竟,泛型方法需要对每个方法都使用完全相同的 ILT - 因此在某些情况下它不能调用您的运算符,而在其他情况下则不能调用类型检查。

此外,由于您已明确转换为object,因此永远不会使用它;强制转换object始终简单的拆箱或类型检查。

一个邪恶的解决方法是(我不喜欢这样):

        this.a.Add((T)(dynamic)"abc");
        this.a.Add((T)(dynamic)"def");
        this.a.Add((T)(dynamic)"ghi");

它将解决方案推迟到运行时。它有效,但在那之后我需要洗眼睛。不过,更一般地说:运算符和泛型不能很好地发挥作用- 所以:尽量不要在您的 API 中使用这种组合。我个人真的不会使用上述内容!

于 2012-08-13T12:19:44.187 回答
2

如果泛型参数具有您正在使用的显式转换,编译器在编译时不知道。

您可以引入接口,并对泛型参数施加约束

于 2012-08-13T12:20:49.897 回答
2

你插入了 (object) cast 来让编译器停止告诉你你做错了。这行得通,编译器不再抱怨,因为这阻止了它检查类型。并且从 object 到 T 的转换实际上可能会起作用,只要几率很小。

但是,您没有想到的是显式转换运算符是 C# 语言功能。只有编译器知道在应用这种强制转换时要执行什么方法。它不是CLR 功能,运行时不会去寻找和收集来尝试找到适用的方法来进行转换。C# 编译器无法使用您在编译时提供的任何运算符,它不知道 T 的类型。

麻烦的是,当强制转换执行时,编译器不再提供帮助。卡布姆。

Having generic types applied at runtime instead of compile time is a .NET feature, the generic term is "reified generics". As opposed to "type erasure", the way generics are implemented in Java and C++. The trouble with type erasure is that all info about the generic type arguments are lost after code is compiled, a generic type can't be used by another language and Reflection doesn't work. The trouble with reified generics is that operations that cannot be universally applied to any type cannot be used. Like operator+(). And like this cast.

于 2012-08-13T12:42:28.613 回答
1

这是因为TforA<T>不知道Bor的显式转换运算符C

于 2012-08-13T12:19:08.167 回答