在c#中将接口转换为T

时间:2012-11-22 07:48:39

标签: c# generics interface casting theory

我有一段感觉像是这样的代码:(我已经删除了这些方法,因为它们对这个问题没有帮助)

public abstract class A { }
public interface I { }
public class C : A , I { }
public class Program
{
    static void Update<T>(List<T> l,A a,I i,C c)
    {

        l.Add((T)a);//Error
        l.Add((T)i);
        l.Add((T)c);//Error
    }
}

对于抽象和具体类,转换在编译时失败,但不是接口。

我知道我可以做l.Add((T)(object)a);之类的东西来欺骗编译器,但是我不明白为什么将接口转换为T工作。(如果它们都出错了那么我可以假设它是某种类型 - 检查错误)

我在java中尝试过,它们都有效。

public static <T> void update(List<T> l,C c,I i,A a){
    l.add((T)c);//ok
    l.add((T)i);//ok
    l.add((T)a);//ok
}

这只是因为c#编译器的方式,或者我在OOP上缺少一些概念?

4 个答案:

答案 0 :(得分:1)

要让编译器理解您的代码,您必须使用关键字where约束T,如下所示:

public abstract class A { }
public interface I { }
public class C : A, I { }
public class Program
{
    static void Update<T>(List<T> l, A a, I i, C c)
        // HERE IS THE CHANGE
        where T: C
    {

        l.Add((T)a);//Error
        l.Add((T)i);
        l.Add((T)c);//Error
    }
}

这是因为T默认情况下可以是一切,甚至不是类,只有在约束后,代码的所有变体才会起作用。

如果你像我提到的那样限制你的方法,你可以编译这个 ,所以你可以做的唯一用途是:

var c = new C();
Update<C>(new List<C>(), c as A, (I)c, c);

无用,您应该重写代码。我认为,你应该使用更多的接口和更少的基类。

答案 1 :(得分:1)

在编译时,不知道实现接口I的类是否实际上可以是T类型,因此编译器不能抱怨。但是T在您的代码中未绑定,并且编译器抱怨T需要声明边界以使代码更安全。

您可以解决编译时安全问题,并希望所有内容都能在呼叫网站上运行,如下所示:

  l.Add((T)(object)a);// no compile time Error but possibly at runtime
  l.Add((T)i);  //no compile time Error but possibly at runtime
  l.Add((T)(object)c);// no compile time Error but possibly at runtime

但是牺牲编译时检查是个坏主意。

而是声明T的绑定,例如:

static void Update<T>(List<T> l, A a, I i, C c)  where T: C {

  l.Add((T)a);// no compile time Error and less likely a runtime error
  l.Add((T)i);  //no compile time Error but still possibly at runtime
  l.Add((T)c);// no compile time Error and less likely a runtime error
}

很容易看出这可能会出错

如果你打电话给Update<string>(stringlist, a, i, c) - 否则会破坏,因为A,I和C都不能被强制转换成字符串。

答案 2 :(得分:0)

将任意类型的项目转换为C#中的泛型类型参数不是一个好习惯。想象一下,有一个不相关的类D并且您调用Update<D>(new List<D>(), new A(), new C()) - 如果您尝试将A的实例添加到List<D>,则会在运行时获得异常。

考虑一个非通用的更简单的实现,您使用某个接口O,而不是通用参数,例如:

public interface O { }
public interface I : O { }
public abstract class A : O { }
public class C : A, I { }

public class Program {
    static void Update(List<O> l, A a, I i, C c) {
        l.Add(a);
        l.Add(i);
        l.Add(c);
    }
}

答案 3 :(得分:0)

参见6.2.7 C#语言规范(“涉及类型参数的显式转换”),有一个解释:

对于给定的类型参数T,存在以下显式转换:

  • 从任何接口类型到T 。在运行时,如果T是值类型,则转换将作为拆箱转换执行。否则,转换将作为显式引用转换或标识转换执行。

上述规则不允许从无约束类型参数直接显式转换为非接口类型,这可能会令人惊讶。此规则的原因是为了防止混淆并使这种转换的语义清晰。例如,请考虑以下声明:

class X<T>
{
   public static long F(T t) {
      return (long)t;            // Error 
   }
}

如果允许将t直接显式转换为int,则可能很容易预期X.F(7)将返回7L。但是,它不会,因为只有在绑定时已知类型为数字时才会考虑标准数字转换。