我有一段感觉像是这样的代码:(我已经删除了这些方法,因为它们对这个问题没有帮助)
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上缺少一些概念?
答案 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,存在以下显式转换:
上述规则不允许从无约束类型参数直接显式转换为非接口类型,这可能会令人惊讶。此规则的原因是为了防止混淆并使这种转换的语义清晰。例如,请考虑以下声明:
class X<T>
{
public static long F(T t) {
return (long)t; // Error
}
}
如果允许将t直接显式转换为int,则可能很容易预期X.F(7)将返回7L。但是,它不会,因为只有在绑定时已知类型为数字时才会考虑标准数字转换。
“