鉴于这样的几种类型:
interface I {}
class C : I {}
如何进行静态类型转换?我的意思是:我如何以在编译时检查的方式更改其类型?
在C ++中,您可以static_cast<I*>(c)
。在C#中,我能做的最好的事情是创建一个备用类型的临时变量并尝试分配它:
var c = new C();
I i = c; // statically checked
但是这会阻止流畅的编程。我必须创建一个新的变量来进行类型检查。所以我已经确定了这样的事情:
class C : I
{
public I I { get { return this; } }
}
现在我可以通过调用c.I
静态将C转换为I.
在C#中有更好的方法吗?
(如果有人想知道我为什么要这样做,那是因为我使用显式接口实现,并且从另一个成员函数中调用其中一个需要首先转换为接口类型,否则编译器找不到方法。)
更新
我想出的另一个选项是对象扩展:
public static class ObjectExtensions
{
[DebuggerStepThrough]
public static T StaticTo<T>(this T o)
{
return o;
}
}
所以((I)c).Doit()
也可能是c.StaticTo<I>().Doit()
。嗯...可能仍然坚持简单演员。想想我还是会发布这个其他选项。
答案 0 :(得分:5)
简单地施展它:
(I)c
编辑示例:
var c = new C();
((I)c).MethodOnI();
答案 1 :(得分:2)
var c = new C();
I i = c; // statically checked
等于
I i = new C();
答案 2 :(得分:2)
编写一个扩展方法,使用您在UPDATE中提到的技巧:
public static class ObjectExtensions
{
public static T StaticCast<T>(this T o) => o;
}
使用:
things.StaticCast<IEnumerable>().GetEnumerator();
如果things
是IEnumerable<object>
,则编译。如果things
为object
,则会失败。
// Compiles (because IEnumerable<char> is known at compiletime
// to be IEnumerable too).
"adsf".StaticCast<IEnumerable>().GetEnumerator();
// error CS1929: 'object' does not contain a definition for 'StaticCast'
// and the best extension method overload
// 'ObjectExtensions.StaticCast<IEnumerable>(IEnumerable)'
// requires a receiver of type 'IEnumerable'
new object().StaticCast<IEnumerable>().GetEnumerator();
重构过程中的一个常见做法是继续进行更改,然后验证您的更改未导致任何回归。您可以通过各种方式和不同阶段检测回归。例如,某些类型的重构可能会导致API更改/破坏,并且需要重构代码库的其他部分。
如果您的代码的一部分期望接收在编译时应该知道的类型(ClassA
)以实现接口(IInterfaceA
)并且该代码想要直接访问接口成员,则可能必须转向接口类型,例如,访问明确实现的接口成员。如果在重构后, ClassA
不再实施IIterfaceA
,则会根据您投放到界面的方式获得不同类型的错误:
((IInterfaceA)MethodReturningClassA()).Act();
突然变成运行时强制转换并抛出运行时错误。IInterfaceA a = MethodReturningClassA(); a.Act();
会引发编译时错误。static_cast<T>
之类的扩展方法:MethodReturningClassA().StaticCast<IInterfaceA>().Act();
会引发编译时错误。如果您希望转换为转发并且在编译时可以验证,那么您应该使用强制编译时验证的强制转换方法。这使得代码的原始开发人员有意清楚地编写类型安全代码。编写类型安全代码的好处是可以在编译时更加可验证。通过做一些工作来阐明你对其他开发人员,你自己和编译器选择类型安全的意图,你神奇地得到了编译器帮助验证你的代码并且可以在之前(在编译时)比以后捕获重构的影响(如果你的代码没有碰巧有完整的测试覆盖率,比如运行时崩溃。)
答案 3 :(得分:1)
如果您真的只是在寻找一种方法来查看对象是否实现了特定类型,那么您应该使用as
。
I i = whatever as i;
if (i == null) // It wasn't
否则,你只是施展它。 (在.NET中没有像C ++那样的多种类型的转换 - 除非你比大多数人需要的更深入,但它更多的是关于WeakReference等等。)
I i = (I)c;
如果您只是想方便地将实现I
的任何内容转换为I
,那么您可以使用扩展方法或类似方法。
public static I ToI(this I @this)
{
return @this;
}