在C#中何时应使用as关键字?

31

大多数情况下,当你想要更改类型时,只需要使用传统的强制转换即可。

var value = (string)dictionary[key];

这很好,因为:

  • 它速度快
  • 它会报错(而不是抛出“对象为空”异常)

那么,有没有一个很好的例子可以使用 as 运算符呢?我无法找到或想到一个完美适合它的例子。

注意:实际上,我认为有时编译器会阻止使用强制类型转换,但可以使用 as 运算符(与泛型有关?)。


1
编译器并不真正“阻止”强制类型转换,它只是在静态确定类型不相关且强制类型转换必定失败时给出错误提示。 - Paolo Tedesco
模式匹配可能是现今更流行的方式。 - Soner from The Ottoman Empire
7个回答

37

当一个对象不是你想要的类型,但它仍然有效时,使用as关键字,并且希望在其为所需类型时有所不同。例如,在伪代码中:

foreach (Control control in foo)
{
    // Do something with every control...

    ContainerControl container = control as ContainerControl;
    if (container != null)
    {
        ApplyToChildren(container);
    }
}

或者在 LINQ to Objects 中进行优化(有很多类似这样的示例):

public static int Count<T>(this IEnumerable<T> source)
{
    IList list = source as IList;
    if (list != null)
    {
        return list.Count;
    }
    IList<T> genericList = source as IList<T>;
    if (genericList != null)
    {
        return genericList.Count;
    }

    // Okay, we'll do things the slow way...
    int result = 0;
    using (var iterator = source.GetEnumerator())
    {
        while (iterator.MoveNext())
        {
            result++;
        }
    }
    return result;
}

使用as就像是一个is加上一个强制转换。通常在上述示例中之后会进行空值检查。


2
语义上,“as is like an is + a cast.”当然是正确的,有趣的是从IL的角度来看,情况并非如此(我相信您知道,但并非所有人都能读懂您的回答)。http://blogs.msdn.com/b/ericlippert/archive/2010/09/16/is-is-as-or-is-as-is.aspx - Rune FS
你有“几乎总是”这个位的示例吗? - Daniel Little
1
@Lavinski:我偶尔会在方法调用中使用as,其中参数可以为null,因此我能够无条件地传递它。不过方法内部可能有一个空值检查 :) 一种变体是与空值合并运算符一起使用,例如:IList<T> list = sequence as IList<T> ?? sequence.ToList(); - Jon Skeet

27

每次需要进行安全转换对象而不出现异常时,请使用as

MyType a = (MyType)myObj; // throws an exception if type wrong

MyType a = myObj as MyType; // return null if type wrong

我认为OP是在询问何时首先使用安全转换。 - Rune FS
3
是的,我回答说:当您需要在没有异常的情况下安全地转换对象时。 - Samich
@Saminch和我的观点是,你没有回答问题“何时需要安全转换”(安全转换首先不会抛出异常)。因此,一个答案应该包括一个场景,在这个场景中,对象是不同类型的是有效的,但如果您能够使用运行时类型的知识,您可能会获得一些东西。请参见Jons的答案以获取示例。 - Rune FS
同意在这里提供用例可能更有帮助。你可以给它投反对票。 - Samich
我不觉得有必要给你的回答点踩。你的回答并没有错误。我只是在提供一个方法,让它变得更好(在我看来)。 - Rune FS

10

As关键字用于避免像下面这样的重复类型转换逻辑:

if (x is MyClass)
{
  MyClass y = (MyClass)x;
}

使用

MyClass y = x as MyClass;
if (y == null)
{
}

供您参考,针对第一种情况生成的IL代码如下:

  // if (x is MyClass)
  IL_0008:  isinst     MyClass
  IL_000d:  ldnull
  IL_000e:  cgt.un
  IL_0010:  ldc.i4.0
  IL_0011:  ceq
  IL_0013:  stloc.2
  IL_0014:  ldloc.2
  IL_0015:  brtrue.s   IL_0020
  IL_0017:  nop
  // MyClass y = (MyClass)x;
  IL_0018:  ldloc.0
  IL_0019:  castclass  MyClass
  IL_001e:  stloc.1

并且对于第二种情况:
  // MyClass y = x as MyClass;
  IL_0008:  isinst     MyClass
  IL_000d:  stloc.1
  // if (y == null)
  IL_000e:  ldloc.1
  IL_000f:  ldnull
  IL_0010:  ceq
  IL_0012:  stloc.2
  IL_0013:  ldloc.2
  IL_0014:  brtrue.s   IL_0018

7

如果使用 as 进行转换失败,将不会引发转换异常,只会返回 null


2

Enumerable中的.Count()方法实现使用了该技术,使得对集合计数更加高效。

具体实现方式如下:

        ICollection<TSource> collection = source as ICollection<TSource>;
        if (collection != null)
        {
            return collection.Count;
        }
        ICollection collection2 = source as ICollection;
        if (collection2 != null)
        {
            return collection2.Count;
        }

尝试将源转换为ICollection或ICollection,两者都具有Count属性。 如果失败,Count()会迭代整个源。 因此,如果您不确定类型并且需要之后的类型对象(如上例所示),则应使用as
如果您只想测试对象是否属于给定类型,请使用is,如果您确定对象属于给定类型(或派生自/实现该类型),则可以进行强制转换。

2

大家的回复都很好,但让我们更加实际一些。在你自己的代码中,即非供应商代码中,AS关键字的真正威力并没有展现出来。

但是在处理WPF / silverlight等供应商对象时,AS关键字确实是一个真正的优势。例如,如果我在Canvas上有一系列控件,并且我想跟踪最后选择的控件,但在单击Canvas时清除跟踪变量,我会这样做:

private void layoutroot_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
               //clear the auto selected control
        if (this.SelectedControl != null 
            && sender is Canvas && e.OriginalSource is Canvas)
        {
            if ((sender as Canvas).Equals(( e.OriginalSource as Canvas)))
            {
                this.SelectedControl = null;
            }
        }
    }

使用AS关键字的另一个原因是当您的类实现了一个或多个接口,并且您只想显式地使用其中一个接口时:

IMySecond obj = new MyClass as IMySecond

虽然这里并不是必需的,但如果 MyClass 没有实现 IMySecond 接口,它会将 null 赋值给变量 obj。


你的代码行 if((sender as Canvas).Equals((e.OriginalSource as Canvas))),我认为即使它们都是 null,也可能返回 true,因为它们都不是 Canvas 类型。 - Kieran Bond

0

这里是来自http://blog.nerdbank.net/2008/06/when-not-to-use-c-keyword.html的代码片段。

class SomeType {
    int someField;
    // The numeric suffixes on these methods are only added for reference later
    public override bool Equals1(object obj) {
        SomeType other = obj as SomeType;
        if (other == null) return false;
        return someField == other.SomeField;
    }
    public override bool Equals2(object obj) {
        if (obj == null) return false;
        // protect against an InvalidCastException
        if (!(obj is SomeType)) return false;
        SomeType other = (SomeType)obj;
        return someField == other.SomeField;
    }
}

上面的 Equals1 方法比 Equals2 更高效(并且更易于阅读),尽管它们完成了相同的工作。虽然 Equals1 编译为执行类型检查和转换一次的 IL,但 Equals2 编译为首先进行 "is" 运算符的类型比较,然后将类型比较和转换作为 () 运算符的一部分一起执行。因此,在这种情况下使用 "as" 实际上更有效率。事实上,易于阅读只是一个额外的奖励。
总之,在预计转换在一种非异常情况下失败时才使用 C# 的 "as" 关键字。如果您指望强制转型成功,并且没有准备好接收任何可能失败的对象,则应使用 () 强制转换运算符,以便引发适当和有用的异常。

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