在c#中什么时候应该使用as关键字?

本文关键字:as 关键字 什么时候 | 更新日期: 2023-09-27 18:09:39

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

var value = (string)dictionary[key];

这很好,因为:

  • 它快速
  • 它会抱怨,如果有什么是错误的(而不是给出对象是null异常)

那么,使用as的一个好例子是什么,我真的找不到或想不出什么完全适合它的东西?

注意:实际上,我认为有时在as工作的情况下,编译器会阻止使用强制转换(与泛型相关?)

在c#中什么时候应该使用as关键字?

使用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 + a强制转换。几乎总是在后面加上null检查,正如上面的例子所示。

每次需要安全强制转换对象时,请使用as:

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

As用于避免双类型转换逻辑,如:

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

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

供参考,为案例#1生成的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

,对于case #2:

  // 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

使用as不会抛出强制转换异常,如果强制转换失败,只返回null

Enumerable中.Count()的实现使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,如果您确定对象为给定类型(或派生/实现该类型),则可以强制转换

好的,大家回答得很好,但是让我们实际一点。在你自己的代码中,即非供应商的代码中,AS关键字的真正力量并不突出。

但是当在WPF/silverlight中处理供应商对象时,as关键字是一个真正的奖励。例如,如果我在画布上有一系列控件,我想跟踪跟踪最后一个选定的控件,但当我点击画布时清除跟踪变量,我将这样做:

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关键字的另一个原因是当你的类实现了1个或多个接口,而你只想显式地使用一个接口:

IMySecond obj = new MyClass as IMySecond

虽然这里没有必要,但如果MyClass没有实现IMySecond

,它将为变量obj分配null

下面是来自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"关键字。如果您指望强制转换成功,并且没有准备好接收任何可能失败的对象,则应该使用()强制转换操作符,以便抛出适当且有用的异常。