1

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

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

4 回答 4

1

为了让编译器理解你的代码,你必须约束Twith 关键字where,像这样:

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);

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

于 2012-11-22T07:57:15.153 回答
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 都不能转换为字符串。

于 2012-11-22T07:57:59.117 回答
0

在 C# 中将任意类型的项转换为泛型类型参数不是一个好习惯。想象有一个不相关的类D并且您调用Update<D>(new List<D>(), new A(), new C())- 如果您尝试将 的实例添加AList<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);
    }
}
于 2012-11-22T08:04:23.997 回答
0

见6.2.7 C#语言规范(“Explicit conversions involved type parameters”),有解释:

"

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

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

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

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

如果允许将 t 直接显式转换为 int,那么人们可能很容易期望 XF(7) 将返回 7L。但是,它不会,因为仅当类型在绑定时已知为数字时才考虑标准数字转换。

"

于 2012-11-22T08:22:15.877 回答