【问题标题】:How to do a static cast in C#?如何在 C# 中进行静态转换?
【发布时间】:2010-10-08 21:18:59
【问题描述】:

给定几个这样的类型:

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&lt;I&gt;().Doit()。嗯......可能仍然会坚持简单的演员表。想我还是会发布这个其他选项。

【问题讨论】:

    标签: c# type-conversion


    【解决方案1】:

    简单地投射它:

    (I)c
    

    编辑示例:

    var c = new C();
    
    ((I)c).MethodOnI();
    

    【讨论】:

    • (facepalm)..这很明显,我为什么不尝试呢?丑陋但有效。谢谢。 :)
    • 现在我想多了,编译器会跳过静态类型检查的一种小方法。假设你从一个对象开始。它可以转换为没有错误的任何东西,而我刚刚在上面添加到问题中的扩展方法StaticTo&lt;T&gt; 会捕获这样一个糟糕的转换。一个小问题。
    【解决方案2】:

    使用您在更新中提到的技巧编写一个扩展方法:

    public static class ObjectExtensions
    {
        public static T StaticCast<T>(this T o) => o;
    }
    

    使用方法:

    things.StaticCast<IEnumerable>().GetEnumerator();
    

    如果things 是,例如IEnumerable&lt;object&gt;,则编译。如果thingsobject,则失败。

    // 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,您会收到不同类型的错误,具体取决于您对接口的转换方式:

    1. C 风格转换:((IInterfaceA)MethodReturningClassA()).Act(); 会突然变成运行时转换并引发运行时错误。
    2. 分配给显式类型变量:IInterfaceA a = MethodReturningClassA(); a.Act(); 会引发编译时错误。
    3. 使用类似static_cast&lt;T&gt; 的扩展方法:MethodReturningClassA().StaticCast&lt;IInterfaceA&gt;().Act(); 会引发编译时错误。

    如果您希望您的转换是向下转换并且在编译时可验证,那么您应该使用强制编译时验证的转换方法。这使代码的原始开发人员编写类型安全代码的意图变得清晰。编写类型安全代码的好处是在编译时更易于验证。通过做一些工作来向其他开发人员、你自己和编译器阐明你选择类型安全的意图,你神奇地得到编译器的帮助来验证你的代码,并且可以更早地(在编译时)捕捉到重构的影响而不是稍后(例如,如果您的代码碰巧没有完整的测试覆盖率,则会导致运行时崩溃)。

    【讨论】:

      【解决方案3】:
      var c = new C(); 
      I i = c;  // statically checked
      

      等于

      I i = new C();
      

      【讨论】:

      • 正确。不幸的是,这并没有解决我的问题。
      【解决方案4】:

      如果您真的只是在寻找一种方法来查看对象是否实现了特定类型,您应该使用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;
      }
      

      【讨论】:

      • 请注意,as 关键字适用于类,但是,这适用于基本类型,例如 bool,因此,如果您需要例如将int 转换为bool,您将不得不使用现在在 C++ 中非常不鼓励使用的丑陋的 C 风格转换(糟糕的 OP 显然没想到在语言中与 C# 一样新)OR 您可以采用上面显示的第二种方法,或使用 OP 更新中提供的简洁模板方法。
      • as 有时会执行动态转换而不会引发编译时错误。你的答案是在最后隐藏真正的宝石;-)
      • @binki: as 被设计为运行时命令。您不希望在 as 最有意义的情况下出现编译时错误。
      • 问题似乎在于如何以一种可以在编译时验证的方式将对象转换为基类/接口。因为as 将在执行编译时和运行时类型检查之间静默切换,所以它不会像C++ 的static_cast&lt;T&gt; 那样安全地强制进行编译时检查。如果您从未打算让演员表需要运行时验证,您会不会宁愿出现编译时错误?
      • 如果您对问题的假设是正确的,那么接受的答案就大错特错了。强制转换也不提供完整的编译时检查。
      猜你喜欢
      • 1970-01-01
      • 2022-06-12
      • 1970-01-01
      • 2019-07-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-30
      相关资源
      最近更新 更多