关于施法:C#“as”施放vs经典施法

C# “as” cast vs classic cast

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Casting vs using the ‘as’ keyword in the CLR

我最近学到了一种不同的演员阵容。而不是使用

1
SomeClass someObject = (SomeClass) obj;

可以使用以下语法:

1
SomeClass someObject = obj as SomeClass;

如果obj不是someclass,则返回空值,而不是抛出类强制转换异常。

我看到,如果强制转换失败,并且我试图访问someobject变量,这可能导致nullreferenceexception。所以我想知道这个方法的基本原理是什么?为什么要使用这种类型的转换而不是(旧的)类型-它似乎只会将失败的类型转换的问题"深入"到代码中。


对于"经典"方法,如果强制转换失败,将引发异常。使用as方法,它将导致可以检查的空值,并避免引发异常。

此外,您只能将"as"与引用类型一起使用,因此如果您要将类型转换为值类型,则必须仍然使用"classic"方法。

注:

as方法只能用于可以分配null值的类型。它只用于表示引用类型,但当.NET 2.0出现时,它引入了可为空值类型的概念。由于这些类型可以分配一个null值,因此它们可以与as运算符一起使用。


空比较比抛出和捕获异常快得多。异常具有显著的开销-必须组装堆栈跟踪等。

异常应该代表一种意外的状态,这种状态通常不代表这种情况(即当as工作得更好时)。


在某些情况下,处理null比处理例外情况更容易。尤其是,合并运算符非常方便:

1
SomeClass someObject = (obj as SomeClass) ?? new SomeClass();

它还简化了基于对象类型的(不使用多态性和)分支的代码:

1
2
3
4
5
6
7
8
9
10
ClassA a;
ClassB b;
if ((a = obj as ClassA) != null)
{
    // use a
}
else if ((b = obj as ClassB) != null)
{
    // use b
}

根据msdn页面的规定,as运算符相当于:

1
expression is type ? (type)expression : (type)null

它完全避免了有利于更快的类型测试的异常,但也限制了它对支持null的类型(引用类型和Nullable的类型)的使用。


as运算符在几种情况下都很有用。

  • 当您只需要知道一个对象是特定类型的,但不需要对该类型的成员执行特定的操作时
  • 当你想避免例外,而不是明确处理null
  • 您想知道对象之间是否存在CLR转换,而不仅仅是一些用户定义的转换。
  • 第三点很微妙但很重要。没有一个1-1映射,在这两个映射之间,CAST运算符将成功执行CAST,而as运算符将成功执行CAST。as运算符严格限制为clr转换,不考虑用户定义的转换(cast运算符将)。

    具体来说,as运算符仅允许以下内容(从C lang规范第7.9.11节)

    • 同一性(§6.1.1)、隐式引用(§6.1.6)、拳击(§6.1.7)、显式引用(§6.2.4)或拆箱(§6.2.5)从E型转换为T型。
    • E或T的类型是开放类型。
    • e是空文本。


    当您真正不知道变量的类型时,as关键字很有用。如果您有一个函数将根据参数的实际类型遵循不同的代码路径,那么您有两个选择:

    首先,使用普通铸件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if(myObj is string)
    {
        string value = (string)myObj;

        ... do something
    }
    else if(myObj is MyClass)
    {
        MyClass = (MyClass)myObj;
    }

    这要求您使用is检查对象的类型,以便不尝试将其强制转换为将失败的对象。这也有点多余,因为is类型检查在强制转换中再次执行(以便在需要时抛出异常)。

    另一种方法是使用as

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    string myString = myObj as string;
    MyClass myClass = myObj as MyClass;

    if(myString != null)
    {

    }
    else if(myClass != null)
    {

    }

    这使得代码略短,并且消除了冗余类型检查。


    我认为最好的"规则"是只在预期你的主题不会是你要投射到的对象时使用"as"关键字:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    var x = GiveMeSomething();

    var subject = x as String;

    if(subject != null)
    {
      // do what you want with a string
    }
    else
    {
      // do what you want with NOT a string
    }

    然而,当你的主题应该是你要铸造的类型,使用"经典铸造",正如你所说。因为如果它不是您期望的类型,您将得到一个符合特殊情况的异常。


    如果不是有效的强制转换,则使用as将返回空值,该强制转换允许您在try/catch中包装强制转换之外执行其他操作。我讨厌经典的演员阵容。如果我不确定,我总是用演员表。另外,例外是昂贵的。空检查不是。


    我认为,如果将强制转换的结果传递给一个方法,而您知道该方法将处理空引用,而不进行抛出和ArgumentNullException或类似的操作,这是很有用的。

    我发现as的用处很小,因为:

    1
    obj as T

    慢于:

    1
    2
    if (obj is T)
        ...(T)obj...

    使用as对我来说是一个非常边缘的场景,所以我不能想到任何一般的规则,当我将它用于仅仅把(更具信息性的)强制转换异常向上扩展时。


    您可以使用"as"语句来避免出现异常的可能性,例如,您可以通过逻辑优雅地处理强制转换失败。只有在确定对象是所需类型时才使用强制转换。我几乎总是使用"as",然后检查是否为空。


    这里什么都没有发生。基本上,测试某个东西看它是否属于某种类型(即使用"a s")是很方便的。您需要检查"as"调用的结果,看看结果是否为空。

    当您希望强制转换工作并且希望抛出异常时,请使用"Classic"方法。