问题描述
给定几个这样的类型:
interface I {}
class C : I {}
如何进行静态类型转换?我的意思是:如何以在编译时检查的方式更改其类型?
How can I do a static type cast? By this I mean: how can I change its type in a way that gets checked at compile time?
在 C++ 中,您可以执行 static_cast<I*>(c)
.在 C# 中,我能做的最好的事情就是创建一个备用类型的临时变量并尝试分配它:
In C++ you can do static_cast<I*>(c)
. In C# the best I can do is create a temporary variable of the alternate type and try to assign it:
var c = new C();
I i = c; // statically checked
但这会妨碍流畅的编程.我必须创建一个新变量来进行类型检查.所以我决定了这样的事情:
But this prevents fluent programming. I have to create a new variable just to do the type check. So I've settled on something like this:
class C : I
{
public I I { get { return this; } }
}
现在我可以通过调用 c.I
将 C 静态转换为 I.
Now I can statically convert C to I by just calling c.I
.
在 C# 中有没有更好的方法来做到这一点?
Is there a better way to do this in C#?
(如果有人想知道我为什么要这样做,那是因为我使用显式接口实现,并且从另一个成员函数中调用其中一个需要先转换为接口类型,否则编译器找不到方法.)
(In case anyone's wondering why I want to do this, it's because I use explicit interface implementations, and calling one of those from within another member function requires a cast to the interface type first, otherwise the compiler can't find the method.)
更新
我想出的另一个选项是对象扩展:
Another option I came up with is an object extension:
public static class ObjectExtensions
{
[DebuggerStepThrough]
public static T StaticTo<T>(this T o)
{
return o;
}
}
所以 ((I)c).Doit()
也可以是 c.StaticTo().Doit()
.嗯......可能仍然会坚持简单的演员表.想我还是会发布这个其他选项.
So ((I)c).Doit()
could also be c.StaticTo<I>().Doit()
. Hmm...probably will still stick with the simple cast. Figured I'd post this other option anyway.
推荐答案
使用您在 UPDATE 中提到的技巧编写一个扩展方法:
Write an extension method that uses the trick you mentioned in your UPDATE:
public static class ObjectExtensions
{
public static T StaticCast<T>(this T o) => o;
}
使用方法:
things.StaticCast<IEnumerable>().GetEnumerator();
如果 things
是,例如 IEnumerable
,则编译.如果 things
是 object
则失败.
If things
is, e.g., IEnumerable<object>
, this compiles. If things
is object
, it fails.
// 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 更改/损坏,并需要重构代码库的其他部分.
Why Use a Static Cast?
One common practice during refactoring is to go ahead and make your changes and then verify that your changes have not caused any regressions. You can detect regressions in various ways and at various stages. For example, some types of refactoring may result in API changes/breakage and require refactoring other parts of the codebase.
如果您的代码的一部分期望接收在编译时应该知道的类型 (ClassA
) 以实现接口 (IInterfaceA
) 并且该代码想要访问直接接口成员,它可能必须强制转换为接口类型,例如,访问显式实现的接口成员.如果在重构之后,ClassA
不再实现 IIterfaceA
,您会得到不同类型的错误,具体取决于您对接口的转换方式:
If one part of your code expects to receive a type (ClassA
) that should be known at compiletime to implement an interface (IInterfaceA
) and that code wants to access interface members directly, it may have to cast down to the interface type to, e.g., access explicitly implemented interface members. If, after refactoring, ClassA
no longer implements IIterfaceA
, you get different types of errors depending on how you casted down to the interface:
- C 风格强制转换:
((IInterfaceA)MethodReturningClassA()).Act();
会突然变成运行时强制转换并抛出运行时错误. - 分配给显式类型变量:
IInterfaceA a = MethodReturningClassA();a.Act();
会引发编译时错误. - 使用类似
static_cast<T>
的扩展方法:MethodReturningClassA().StaticCast<IInterfaceA>().Act();
会引发编译时错误.
- C-style cast:
((IInterfaceA)MethodReturningClassA()).Act();
would suddenly become a runtime cast and throw a runtime error. - Assigning to an explicitly-typed variable:
IInterfaceA a = MethodReturningClassA(); a.Act();
would raise a compiletime error. - Using the
static_cast<T>
-like extension method:MethodReturningClassA().StaticCast<IInterfaceA>().Act();
would raise a compiletime error.
如果您希望您的转换是向下转换并且在编译时可验证,那么您应该使用强制编译时验证的转换方法.这使代码的原始开发人员编写类型安全代码的意图变得清晰.编写类型安全代码的好处是在编译时更易于验证.通过做一些工作来向其他开发人员、你自己和编译器阐明你选择类型安全的意图,你神奇地得到编译器的帮助来验证你的代码,并且可以更早地(在编译时)捕捉到重构的影响而不是稍后(例如,如果您的代码碰巧没有完整的测试覆盖率,则会导致运行时崩溃).
If you expected your cast to be a downcast and to be verifiable at compiletime, then you should use a casting method that forces compiletime verification. This makes the intentions of the code’s original developer to write typesafe code clear. And writing typesafe code has the benefit of being more verifiable at compiletime. By doing a little bit of work to clarify your intention to opt into typesafety to both other developers, yourself, and the compiler, you magically get the compiler’s help in verifying your code and can catch repercussions of refactoring earlier (at compiletime) than later (such as a runtime crash if your code didn’t happen to have full test coverage).
这篇关于如何在 C# 中进行静态转换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!