C#中用于类型转型的最佳实践是哪种?

这个问题在这里已经有了答案:

  • 直接投射vs'as'运营商? 15个答案
  • 是和关键字14答案之间的区别

  • 至少有两种铸造可能性和一种用于类型检查。 每个人都有自己的目的,这取决于具体情况:

    硬铸

    var myObject = (MyType)source;
    

    如果您完全确定给定的对象是否属于该类型,通常会这样做。 如果您订阅了事件处理程序并将发件人对象转换为正确的类型来处理它,则可以使用它。

    private void OnButtonClick(object sender, EventArgs e)
    {
        var button = (Button)sender;
    
        button.Text = "Disabled";
        button.Enabled = false;
    }
    

    软铸

    var myObject = source as MyType;
    
    if (myObject != null)
        // Do Something
    

    如果你不知道你是否真的有这种类型,通常会使用它。 所以只需简单地将它投射出去,如果不可能的话,简单地给一个null。 一个常见的例子是,如果您只有在满足某个界面的情况下才需要执行某些操作:

    var disposable = source as IDisposable;
    
    if(disposable != null)
        disposable.Dispose();
    

    类型检查

    var isMyType = source is MyType;
    

    这很少正确使用。 这种类型检查仅在您需要知道某个特定类型的东西时才有用,但您不必使用该对象。

    if(source is MyType)
       DoSomething();
    else
       DoSomethingElse();
    

    我认为这是一个很好的问题,值得认真和详细的答案。 类型转换是C#实际上有很多不同的东西。

    与C#不同,像C ++这样的语言对这些非常严格,所以我将使用那里的命名作为参考。 我总是认为最好理解事情是如何运作的,所以我会在这里详细介绍它。 开始:

    动态转换和静态转换

    C#具有值类型和引用类型。 引用类型始终遵循继承链,从Object开始。

    基本上,如果你这样做(Foo)myObject ,你实际上正在做一个动态转换,如果你正在做(object)myFoo (或者简单地object o = myFoo ),你正在做一个静态转换。

    动态转换需要您进行类型检查,也就是说,运行时将检查您正在投射的对象是否属于该类型。 毕竟,你正在抛弃继承树,所以你不妨完全抛弃其他东西。 如果是这种情况,最终会出现InvalidCastException 。 因此,动态转换需要运行时类型信息(例如,它要求运行时知道哪个对象具有什么类型)。

    静态转换不需要类型检查。 在这种情况下,我们正在继承树中进行投射,所以我们已经知道类型转换会成功。 没有例外会被抛出,永远。

    值类型转换是一种特殊类型的转换,它将不同的值类型(f.ex.从float转换为int)转换。 我将在稍后讨论。

    因为,是演员

    在IL中,唯一受支持的是castclass (cast)和isinst (as)。 该is经营者为实现as一个空检查,无非是对他们两人的结合方便的速记符号更多。 在C#中,你可以写is因为: (myObject as MyFoo) != null

    as检查对象是否是特定类型一样,如果不是则返回null。 对于静态转换的情况,我们可以确定这个编译时间,对于动态转换的情况,我们必须在运行时检查这个情况。

    (...)再次检查类型是否正确,如果不是,则抛出异常。 它基本上和as相同,但是用throw而不是null结果。 这可能会让你想知道为什么as不能作为异常处理程序实现 - 这可能是因为异常相对较慢。

    拳击

    当您在特型演员的发生box的值类型为对象。 基本上发生的事情是,.NET运行时将你的值类型复制到堆上(有一些类型信息),并将地址作为引用类型返回。 换句话说:它将值类型转换为引用类型。

    当你有这样的代码时会发生这种情况:

    int n = 5;
    object o = n; // boxes n
    int m = (int)o; // unboxes o
    

    拆箱需要您指定类型。 在拆箱操作期间,类型被检查(类似于动态转换的情况,但它更简单,因为值类型的继承链很简单),并且如果类型匹配,则将值复制回堆栈。

    你可能期望值类型转换为隐式的 - 因为上面他们不是。 允许的唯一拆箱操作是对精确值类型进行拆箱。 换一种说法:

    sbyte m2 = (sbyte)o; // throws an error
    

    值类型转换

    如果你将一个float为一个int ,那么你基本上是在转换这个值。 对于基本类型(IntPtr,(u)int 8/ conv_* ,float,double),这些转换在IL中预先定义为conv_*指令,这些指令等同于位转换(int8 - > int16),截断(int16 - > int8)和转换(float - > int32)。

    这里有一些有趣的事情发生在这里。 运行时似乎可以处理堆栈中的大量32位值,因此即使在您不希望它们的地方也需要进行转换。 例如,考虑:

    sbyte sum = (sbyte)(sbyte1 + sbyte2); // requires a cast. Return type is int32!
    int sum = int1 + int2; // no cast required, return type is int32.
    

    标志扩展可能会比较棘手,可能会让你头晕目眩。 计算机将有符号的整数值存储为1补码。 以十六进制表示法int8,这意味着值-1是0xFF。 那么如果我们将它转​​换为int32,会发生什么? 同样,-1的补码值为0xFFFFFFFF - 所以我们需要将最重要的位传播到其余的'增加'位。 如果我们正在做无符号扩展,我们需要传播零。

    为了说明这一点,下面是一个简单的测试用例:

    byte b1 = 0xFF;
    sbyte b2 = (sbyte)b1;
    Console.WriteLine((int)b1);
    Console.WriteLine((int)b2);
    Console.ReadLine();
    

    第一次强制转换为int在这里是零扩展,第二次强制转换为int是符号扩展。 您也可能想要使用“x8”格式的字符串来获取十六进制输出。

    对于位转换,截断和转换之间的确切区别,我参考LLVM文档来解释差异。 查找sext / zext / bitcast / fptosi和所有变体。

    隐式类型转换

    还有一个类别,那就是转换操作员。 MSDN详细介绍了如何重载转换运算符。 基本上你可以做的是通过重载一个操作符来实现你自己的转换。 如果您希望用户明确指定您打算投射,请添加explicit关键字; 如果您希望隐式转换自动发生,您可以添加implicit式转换。 基本上你会得到:

    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        return d.value;  // implicit conversion
    }
    

    ...之后你可以做类似的东西

    Digit d = new Digit(123);
    byte b = d;
    

    最佳实践

    首先,了解差异,这意味着要实施小型测试程序,直到您理解上述所有内容之间的区别为止。 没有理解如何工作的代理人。

    然后,我会坚持这些做法:

  • 这些短裤是有原因的。 使用最短的符号,可能是最好的。
  • 不要使用强制转换施放; 只能使用强制转换施放。
  • 只有在需要时才使用拳击。 这个细节远远超出了这个答案。 基本上我说的是:使用正确的类型,不要包装所有东西。
  • 注意有关隐式转换的编译器警告(f.ex. unsigned / signed),并始终使用显式强制转换解决它们。 由于符号/零扩展名,您不希望得到具有奇怪值的惊喜。
  • 在我看来,除非您确切知道自己在做什么,否则最好避免隐式/显式转换 - 一个简单的方法调用通常会更好。 原因是你可能最终会遇到一个例外情况,那就是你没有看到来。

  • 使用第二种方法,如果转换失败,则引发异常。

    使用as投射时,只能使用参考类型。 所以如果你将类型转换为值类型,你仍然必须使用int e = (int) o; 方法。

    一个好的经验法则是:如果您可以将null指定为对象的值,则可以使用as键入cast。

    也就是说,null比较比抛出和捕获异常要快,所以在大多数情况下,使用as应该会更快。

    我不能诚实地说,这是否适用于您is检查。 在另一个线程更改要投射的对象的某些多线程条件下,它可能会失败。

    链接地址: http://www.djcxy.com/p/73729.html

    上一篇: Which is the best practice in C# for type casting?

    下一篇: What is difference between normal typecasting and using “AS” keyword