在阅读Microsoft文档时,我偶然发现了一个有趣的代码示例:
interface ISomeInterface
{...}
class SomeClass
{...}
class MyClass<T>
{
void SomeMethod(T t)
{
ISomeInterface obj1 = (ISomeInterface)t;//Compiles
SomeClass obj2 = (SomeClass)t; //Does not compile
}
}
这意味着除非有约束,否则可以显式地将通用转换为接口而不是类。好吧,我仍然无法理解决策背后的逻辑,因为接口和类类型转换都抛出异常,那么为什么只能防止这些异常中的一个呢?
BTW-有一种绕过编译错误的方法,但这并没有消除我头脑中的逻辑混乱:
class MyOtherClass
{...}
class MyClass<T>
{
void SomeMethod(T t)
{
object temp = t;
MyOtherClass obj = (MyOtherClass)temp;
}
}
答案 0 :(得分:5)
当你尝试在没有继承关系的类之间进行转换时,这正是你在正常情况下得到的 - 没有泛型 -
public interface IA
{
}
public class B
{
}
public class C
{
}
public void SomeMethod( B b )
{
IA o1 = (IA) b; <-- will compile
C o2 = (C)b; <-- won't compile
}
因此,如果没有约束,泛型类的行为就好像类之间没有关系一样。
<强>续... 强>
好吧,让我们说有人这样做:
public class D : B, IA
{
}
然后打电话:
SomeMethod( new D() );
现在你将看到为什么编译器允许接口转换通过。在编译时,如果实现了接口,它实际上无法知道。
请记住,D类可能很好地由使用你的程序集的人编写 - 在编译之后的几年。所以编译器不可能拒绝编译它。必须在运行时检查它。
答案 1 :(得分:2)
最大的区别是接口保证是参考类型。价值类型是麻烦制造者。它在C#语言规范第6.2.6章中有明确提及,其中有一个很好的例子来说明问题:
上述规则不允许从无约束类型参数直接显式转换为非接口类型,这可能会令人惊讶。此规则的原因是为了防止混淆并使这种转换的语义清晰。例如,请考虑以下声明:
class X<T>
{
public static long F(T t) {
return (long)t; // Error
}
}
如果允许将t直接显式转换为int,则可能很容易预期X.F(7)将返回7L。但是,它不会,因为仅在编译时已知类型为数字时才考虑标准数字转换。为了使语义清晰,必须编写上面的例子:
class X<T>
{
public static long F(T t) {
return (long)(object)t; // Ok, but will only work when T is long
}
}
此代码现在将编译,但执行X.F(7)会在运行时抛出异常,因为盒装的int不能直接转换为long。
答案 2 :(得分:1)
没有错。唯一的区别是,在第一种情况下,编译器可以在编译时检测哪里没有可能的强制转换,但他不能对接口如此“确定”,所以错误,在这种情况下,只会在运行时上升。所以,
// Compiles
ISomeInterface obj1 = (ISomeInterface)t;
// Сompiles too!
SomeClass obj2 = (SomeClass)(object)t;
会在运行时产生相同的错误。
原因可能是:编译器不知道哪个接口类实现,但它知道类继承(因此(SomeClass)(object)t
方法有效)。换句话说:在CLR中禁止无效转换,唯一的区别是在某些情况下它可以在编译时检测到,而在某些情况下 - 不能。这背后的主要原因是,即使编译器知道所有类的接口,它也不知道它的后代,它可以实现它,并且对T
有效。请考虑以下情况:
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
MyClass<SomeClass> mc = new MyClass<SomeClass>();
mc.SomeMethod(new SomeClassNested());
}
}
public interface ISomeInterface
{
}
public class SomeClass
{
}
public class SomeClassNested : SomeClass, ISomeInterface
{
}
public class MyClass<T>
{
public void SomeMethod(T t)
{
// Compiles, no errors at runtime
ISomeInterface obj1 = (ISomeInterface)t;
}
}
}
答案 3 :(得分:0)
我认为铸造界面和铸造之间的区别 一个类在于c#支持多个“继承”这一事实 仅适用于接口。那是什么意思?编译器只能 在编译时确定演员表是否对某个类有效 因为C#不允许对类进行多重继承。
另一方面,编译器在编译时不知道是否
不是你的类实现了强制转换中使用的接口。为什么?
有人可以继承你的类并实现接口
用于你的演员。因此,编译器在编译时并未意识到这一点。
(参见下面的SomeMethod4()
)。
然而,编译器能够确定是否 如果您的课程被密封,则演员到界面是有效的。
考虑以下示例:
interface ISomeInterface
{}
class SomeClass
{}
sealed class SealedClass
{
}
class OtherClass
{
}
class DerivedClass : SomeClass, ISomeInterface
{
}
class MyClass
{
void OtherMethod(SomeClass s)
{
ISomeInterface t = (ISomeInterface)s; // Compiles!
}
void OtherMethod2(SealedClass sc)
{
ISomeInterface t = (ISomeInterface)sc; // Does not compile!
}
void OtherMethod3(SomeClass c)
{
OtherClass oc = (OtherClass)c; // Does not compile because compiler knows
} // that SomeClass does not inherit from OtherClass!
void OtherMethod4()
{
OtherMethod(new DerivedClass()); // In this case the cast to ISomeInterface inside
} // the OtherMethod is valid!
}
仿制药也是如此。
希望,这有帮助。