C#中"As"关键字的作用是什么?

65

从文档中得知:

与强制类型转换类似,as 运算符可以将一个对象转换为指定类型。不同的是当转换失败时,它返回 null 而非抛出异常。更准确地说,以下表达式:

   expression as type

等同于:

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

那么为什么不选择一种方式呢?为什么要有两个转换系统?

除了表达式只被评估一次这一点略有不同。


1
你在编辑时弄错了些东西:代码示例不见了。 - tonio
4
关于它的最好部分是... int x=SomeObject as int? ?? 0。(翻译说明:原文已经非常简洁明了,为了保持其原意和准确性,我的翻译仅做必要的语法调整。) - Earlz
相关:https://dev59.com/Z3I95IYBdhLWcg3wzhd9 - Jla
7个回答

95
他们不是两种转换系统。两者具有相似的操作,但意义却截然不同。"as"表示“我认为这个对象可能实际上属于另一种类型;如果不是,请给我null。” 转换则有两种可能的含义:
  • 我确信这个对象实际上属于另一种类型。将其转换为该类型,如果我错了,则崩溃程序。

  • 我确信这个对象不属于另一种类型,但是有一种公认的方法可以将当前类型的值转换为所需类型。(例如,将int转换为short。)将其转换为该类型,如果转换实际上无法完成,则崩溃程序。

请参阅我的文章以获取更多详细信息。

https://ericlippert.com/2009/10/08/whats-the-difference-between-as-and-cast-operators/


4
对于C++程序员来说,C#中的as与C++中的dynamic_cast类似。 - kevinarpe
1
博客链接已失效,但这里有 Microsoft 文档页面: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/type-testing-and-cast - Beetle
@Beetle:谢谢您的留言。我已更新链接。 - Eric Lippert

15

效率和性能

执行类型转换的一部分是一些集成的类型检查; 因此,在实际强制类型转换之前加上显式类型检查是多余的(类型检查会发生两次)。使用as关键字可以确保只执行一次类型检查。您可能会想“但它必须进行空值检查而不是第二个类型检查”,但与类型检查相比,空值检查非常高效且性能卓越。

if (x is SomeType )
{
  SomeType y = (SomeType )x;
  // Do something
}

相较而言,进行了两次检查

SomeType y = x as SomeType;
if (y != null)
{
  // Do something
}

进行1x -- 空值检查与类型检查相比要更加廉价。


4
为什么里面有一个typeof?这没有任何意义。 - Eric Lippert
@Eric -- 理解了!我的错,已经发布了。 - STW
1
@Dykam (x is SomeType) 的实际类型比较执行了1次类型检查,第二次是作为转换过程的一部分执行的(因为它需要找出类型是否兼容,以便在必要时抛出异常) - STW
这就解释了为什么它是CLR特性,但不是语言特性。对于前一种情况,编译器很容易识别并在内部转换为单个检查。 - Ken
@Ken 不确定编译器是否应该在其优化的一部分中执行此操作,但这是FxCop将标记的模式。 - STW

14

有时候您希望如果无法按预期进行转换,则使事情失败,而其他时候则不关心,并且只想丢弃给定对象(如果无法进行转换的话)。

这基本上是常规类型转换的更快版本,包裹在 try 块中;但 As 更易读,而且还能节省打字时间。


这就解释了为什么这个功能存在于类型转换中,但并不解释为什么它不适用于其他十几种事物。我经常希望语言中的其他内容也能静默返回 null,比如字典查找或枚举解析。为什么类型转换如此特殊呢? - Ken
3
字典和枚举都有安全返回值的方法,分别称为TryGetValue和TryParse。 - digEmAll
Amber,我只是好奇 as 如何实现其 try/catch——您的回答和 TomTom 的回答在这个细节上有些冲突。 - STW
1
STW:“基本上是更快的版本”并不是字面意义上的try块,而只是相当于可以使用try块完成的操作。 - Amber
丹尼尔:是的,现在它有了,在4.0版本中。他们花了8年多时间才添加这个功能。 - Ken
digEmAll: 当然,但那些只是方法,不是拥有自己保留字的特殊语法。为什么强制转换会得到特殊的语法呢?(理论上,您可以编写一个使用内部转换的扩展方法 As<T>(object),对吗?) - Ken

7

它允许快速检查,无需尝试/转换开销,这在某些情况下可能需要处理基于消息的继承树。

我经常使用它(获取消息,对特定子类型做出反应)。尝试/转换将显着变慢(每个通过的消息上有许多try/catch框架)- 我们谈论的是每秒处理200,000条消息。


3
只是好奇你或者Amber关于try/catch的问题谁是正确的——听起来as确实会将操作包装在Try块中,但也许使用了更高效的转换机制? - STW
1
@STW:我也是这么想的...如果你有自己的try/catch或者as有自己的try/catch,性能应该是可比的。 - Nelson Rothermel
谈到try/cast的开销,请看这里(附C#团队的道歉):https://dev59.com/4Gox5IYBdhLWcg3w3X5S#8947323 - nurettin

3

让我给你一些真实世界的场景,告诉你在哪些情况下会同时使用它们。

public class Foo
{
  private int m_Member;

  public override bool Equals(object obj)
  {
    // We use 'as' because we are not certain of the type.
    var that = obj as Foo;
    if (that != null)
    {
      return this.m_Member == that.m_Member;
    }
    return false;   
  }
}

而...

public class Program
{
  public static void Main()
  {
    var form = new Form();
    form.Load += Form_Load;
    Application.Run(form);
  }

  private static void Form_Load(object sender, EventArgs args)
  {
    // We use an explicit cast here because we are certain of the type
    // and we want an exception to be thrown if someone attempts to use
    // this method in context where sender is not a Form.
    var form = (Form)sender;

  }
}

2

通常我根据代码的语义选择其中一种。

例如,如果您有一个对象,您知道它必须是字符串,则使用 (string),因为这表明编写代码的人确定该对象是一个字符串,如果不是,则我们已经有比运行时转换异常更大的问题。

如果您不确定对象是否属于特定类型但希望对其进行逻辑处理,则使用 as。您可以使用 is 运算符后跟转换,但 as 运算符更有效率。


0

或许举例会更有帮助:

// Regular casting
Class1 x = new Class1();
Class2 y = (Class2)x; // Throws exception if x doesn't implement or derive from Class2

// Casting with as
Class2 y = x as Class2; // Sets y to null if it can't be casted.  Does not work with int to short, for example.

if (y != null)
{
  // We can use y
}

// Casting but checking before.
// Only works when boxing/unboxing or casting to base classes/interfaces
if (x is Class2)
{
  y = (Class2)x; // Won't fail since we already checked it
  // Use y
}

// Casting with try/catch
// Works with int to short, for example.  Same as "as"
try
{
  y = (Class2)x;
  // Use y
}
catch (InvalidCastException ex)
{
  // Failed cast
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接