Which is the best practice in C# for type casting?
哪种方法是进行类型铸造和检查的最佳实践?
1 2 3 4 5 | Employee e = o as Employee; if(e != null) { //DO stuff } |
或
1 2 3 4 5 |
至少有两种铸造的可能性,一种用于类型检查,二者的组合称为模式匹配。每个人都有自己的目的,这取决于情况:
硬石膏1 | var myObject = (MyType)source; |
如果您完全确定给定对象是否属于该类型,则通常会这样做。如果您订阅了一个事件处理程序,并将发送方对象强制转换为正确的类型,那么就需要使用它。
1 2 3 4 5 6 7 | private void OnButtonClick(object sender, EventArgs e) { var button = (Button)sender; button.Text ="Disabled"; button.Enabled = false; } |
软铸件
1 2 3 4 | var myObject = source as MyType; if (myObject != null) // Do Something |
如果你不知道你是否真的有这种类型,这通常会被使用。因此,只需尝试强制转换它,如果不可能,只需返回一个空值。一个常见的例子是,如果只有在某些接口已满的情况下,您才需要做一些事情:
1 2 3 4 | var disposable = source as IDisposable; if(disposable != null) disposable.Dispose(); |
另外,
1 |
这很少被正确使用。只有当您只需要知道某个对象是否属于特定类型,但不必使用该对象时,此类型检查才有用。
1 2 3 4 |
模式匹配
1 2 |
模式匹配是dotnet框架中与强制转换相关的最新功能。但您也可以使用switch语句和when子句来处理更复杂的情况:
1 2 3 4 5 6 7 | switch (source) { case SpecialType s when s.SpecialValue > 5 DoSomething(s); case AnotherType a when a.Foo =="Hello" SomethingElse(a); } |
我认为这是一个很好的问题,应该得到一个严肃而详细的答案。类型转换实际上是很多不同的东西。好的。
与C语言不同的是,C++语言对这些非常严格,所以我将把它命名为引用。我一直认为最好能理解事情是如何运作的,所以我将在这里为您详细介绍一下。下面是:好的。
动态铸造和静态铸造好的。
C具有值类型和引用类型。引用类型始终遵循继承链,从对象开始。好的。
基本上,如果你做
动态强制转换要求您执行类型检查,即运行时将检查要强制转换的对象是否属于该类型。毕竟,你要抛弃继承树,所以你最好完全抛弃其他东西。如果是这样的话,你最终会得到一个
静态强制转换不需要类型检查。在本例中,我们将在继承树中进行强制转换,因此我们已经知道类型强制转换将成功。永远不会有例外。好的。
值类型转换是一种特殊类型的转换,它将不同的值类型(例如,从float转换为int)。我稍后再谈。好的。
AS,IS,CAST好的。
在IL中,只支持EDOCX1(cast)和EDOCX1(as)。
拳击好的。
当将值类型
当您有这样的代码时,就会发生这种情况:好的。
1 2 3 | int n = 5; object o = n; // boxes n int m = (int)o; // unboxes o |
取消装箱要求您指定类型。在取消装箱操作过程中,会检查类型(如动态强制转换案例,但由于值类型的继承链很小,因此操作简单得多),如果类型匹配,则会将值复制回堆栈。好的。
您可能期望值类型强制转换对于装箱是隐式的——因为上面没有。唯一允许的拆箱操作是拆箱到确切的值类型。换言之:好的。
1 | sbyte m2 = (sbyte)o; // throws an error |
值类型转换好的。
如果您将
顺便说一下,这里发生了一些有趣的事情。运行时似乎可以处理堆栈上的多个32位值,因此即使在不希望进行转换的地方,也需要进行转换。例如,考虑:好的。
1 2 | 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的1补码值是0xffffffff-所以我们需要将最重要的位传播到"添加"位的其余部分。如果我们正在执行无符号扩展,则需要传播零。好的。
为了说明这一点,下面是一个简单的测试用例:好的。
1 2 3 4 5 | byte b1 = 0xFF; sbyte b2 = (sbyte)b1; Console.WriteLine((int)b1); Console.WriteLine((int)b2); Console.ReadLine(); |
第一个到int的转换在这里是零扩展,第二个到int的转换是符号扩展。您可能还需要使用"x8"格式字符串来获得十六进制输出。好的。
对于位转换、截断和转换之间的确切区别,我参考解释这些区别的LLVM文档。查找
隐式类型转换好的。
还有一个类别,那就是转换操作符。msdn详细介绍了如何重载转换运算符。基本上,您可以通过重载一个运算符来实现自己的转换。如果希望用户明确指定要强制转换,则添加
1 2 3 4 | public static implicit operator byte(Digit d) // implicit digit to byte conversion operator { return d.value; // implicit conversion } |
…之后你就可以做像好的。
1 2 |
最佳实践好的。
首先,理解这些差异,这意味着要实现小的测试程序,直到您理解上述所有之间的区别。没有替代品来理解这些东西是如何工作的。好的。
然后,我会坚持这些做法:好的。
- 速记员在场是有原因的。用最短的符号,可能是最好的。
- 不要将强制转换用于静态强制转换;只将强制转换用于动态强制转换。
- 只有在你需要的时候才使用拳击。这方面的细节远远超出了这个答案;基本上我要说的是:使用正确的类型,不要把所有东西都包装起来。
- 注意编译器关于隐式转换(例如,unsigned/signed)的警告,并始终使用显式转换来解决这些警告。您不想因为符号/零扩展而对奇怪的值感到惊讶。
- 在我看来,除非你确切知道自己在做什么,否则最好简单地避免隐式/显式转换——简单的方法调用通常更好。这样做的原因是,你可能最终会得到一个松散的例外,而你没有看到它的到来。
好啊。
嗯,这是一个品味和具体的问题,你正在处理。让我们来看两个使用泛型方法的示例。
对于具有"class"约束的泛型方法(使用双重强制转换的最安全方法):
1 2 3 4 5 6 7 8 9 10 | public void MyMethod<T>(T myParameter) where T : class { if(myParameter is Employee) { // we can use 'as' operator because T is class Employee e = myParameter as Employee; //DO stuff } } |
您也可以这样做(这里有一个强制转换操作,但定义了类型的变量可能正确,也可能不正确):
1 2 3 4 5 6 7 8 | public void MyMethod<T>(T myParameter) where T : class { Employee e; if((e = myParameter as Employee) != null) { //DO stuff with e } } |
对于带有"struct"约束的泛型方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public void MyMethod<T>(T myParameter) where T : struct { if(myParameter is int) { // we cant use 'as' operator here because ValueType cannot be null // explicit conversion doesn't work either because T could be anything so : int e = Convert.ToInt32(myParameter); //DO stuff } } |
带有显式强制转换的简单方案:
1 2 3 | int i = 5; object o = (object)i; // boxing int i2 = (int)o; // unboxing |
我们可以在这里使用显式强制转换,因为我们100%确定要使用什么类型。
如果我需要在投射后使用对象,我将使用
In general, the as operator is more efficient because it actually returns the cast value if the cast can be made successfully. The is operator returns only a Boolean value. It can therefore be used when you just want to determine an object's type but do not have to actually cast it.
(此处提供更多信息)。
我不确定,但我认为
对于第二个方法,如果强制转换失败,将引发异常。
使用
一个很好的经验法则是:如果可以将空值作为值分配给对象,那么可以使用
也就是说,空比较比抛出和捕获异常更快,所以在大多数情况下,使用
不过,我不能确定这是否适用于您的