在CLR中投放与使用'as'关键字

在对接口进行编程时,我发现我正在作大量的转换或对象类型转换。 编程

这两种转换方法之间有区别吗? 若是是这样,是否存在成本差别,或者这对个人计划有何影响? 安全

public interface IMyInterface
{
    void AMethod();
}

public class MyClass : IMyInterface
{
    public void AMethod()
    {
       //Do work
    }

    // Other helper methods....
}

public class Implementation
{
    IMyInterface _MyObj;
    MyClass _myCls1;
    MyClass _myCls2;

    public Implementation()
    {
        _MyObj = new MyClass();

        // What is the difference here:
        _myCls1 = (MyClass)_MyObj;
        _myCls2 = (_MyObj as MyClass);
    }
}

另外,“通常”是什么首选方法? dom


#1楼

看一下这些连接: 性能

他们向您展现了一些细节和性能测试。 测试


#2楼

请忽略乔恩·斯凯特(Jon Skeet)的建议,从新:避免采用测试和广播模式,即: 优化

if (randomObject is TargetType)
{
    TargetType foo = randomObject as TargetType;
    // Do something with foo
}

花费比强制转换和null测试高的想法是错误的: spa

TargetType convertedRandomObject = randomObject as TargetType;
if (convertedRandomObject != null)
{
    // Do stuff with convertedRandomObject
}

这是一个微优化,不起做用。 我运行了一些真实的测试 ,而且test-cast的速度实际上比cast-and-null的比较快,并且它也更安全,由于在if是否应该进行强制类型转换的范围内,您不可能有null引用失败。 pwa

若是您想知道试播法更快或更慢的缘由,则有一个简单而复杂的缘由。 code

简单:即便是天真的编译器也能够将两个相似的操做(如测试和投射)合并到一个测试和分支中。 cast-and-null-test能够强制执行两个测试和一个分支,一个用于类型测试并在失败时转换为null,一个用于null检查自己。 至少,它们都将针对单个测试和分支进行优化,所以测试和发布不会比强制测试和无效测试更快或更慢。 orm

复杂:为何测试和强制转换会更快:强制测试和空检测将另外一个变量引入了外部范围,编译器必须跟踪该变量的活动性,而且视控件的复杂程度而定,它可能没法优化该变量,流是。 相反,“测试和广播”仅在定界范围内引入了新变量,所以编译器知道该变量在范围退出后已死,所以能够更好地优化寄存器分配。

所以,请让这个“ cast-and-null-test优于test-and-cast”建议DIE。 请。 测试和发布既安全又快捷。


#3楼

个人答案只是关于速度,若是咱们不检查类型而且在转换后不检查null的状况下。 我在Jon Skeet的代码中添加了两个附加测试:

using System;
using System.Diagnostics;

class Test
{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];

        for (int i = 0; i < Size; i++)
        {
            values[i] = "x";
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);

        FindLengthWithCast(values);
        FindLengthWithAs(values);

        Console.ReadLine();
    }

    static void FindLengthWithIsAndCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = (string)o;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithIsAndAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            if (o is string)
            {
                string a = o as string;
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("Is and As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAsAndNullCheck(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            if (a != null)
            {
                len += a.Length;
            }
        }
        sw.Stop();
        Console.WriteLine("As and null check: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
    static void FindLengthWithCast(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = (string)o;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("Cast: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }

    static void FindLengthWithAs(object[] values)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int len = 0;
        foreach (object o in values)
        {
            string a = o as string;
            len += a.Length;
        }
        sw.Stop();
        Console.WriteLine("As: {0} : {1}", len,
                          (long)sw.ElapsedMilliseconds);
    }
}

结果:

Is and Cast: 30000000 : 88
Is and As: 30000000 : 93
As and null check: 30000000 : 56
Cast: 30000000 : 66
As: 30000000 : 46

不要像我同样专一于速度,由于全部这些都很是快。


#4楼

除了这里已经介绍的全部内容外,我还发现了一个我认为值得注意的实际区别,即显式强制转换

var x = (T) ...

与使用as运算符相比。

这是示例:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(GenericCaster<string>(12345));
        Console.WriteLine(GenericCaster<object>(new { a = 100, b = "string" }) ?? "null");
        Console.WriteLine(GenericCaster<double>(20.4));

        //prints:
        //12345
        //null
        //20.4

        Console.WriteLine(GenericCaster2<string>(12345));
        Console.WriteLine(GenericCaster2<object>(new { a = 100, b = "string" }) ?? "null");

        //will not compile -> 20.4 does not comply due to the type constraint "T : class"
        //Console.WriteLine(GenericCaster2<double>(20.4));
    }

    static T GenericCaster<T>(object value, T defaultValue = default(T))
    {
        T castedValue;
        try
        {
            castedValue = (T) Convert.ChangeType(value, typeof(T));
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }

    static T GenericCaster2<T>(object value, T defaultValue = default(T)) where T : class
    {
        T castedValue;
        try
        {
            castedValue = Convert.ChangeType(value, typeof(T)) as T;
        }
        catch (Exception)
        {
            castedValue = defaultValue;
        }

        return castedValue;
    }
}

底线: GenericCaster2不适用于结构类型。 GenericCaster将。


#5楼

若是使用针对.NET Framework 4.X的Office PIA,则应使用as关键字,不然它将没法编译。

Microsoft.Office.Interop.Outlook.Application o = new Microsoft.Office.Interop.Outlook.Application();
Microsoft.Office.Interop.Outlook.MailItem m = o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem) as Microsoft.Office.Interop.Outlook.MailItem;

定位.NET 2.0时, 投射能够:

Microsoft.Office.Interop.Outlook.MailItem m = (Microsoft.Office.Interop.Outlook.MailItem)o.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olMailItem);

以.NET 4.X为目标时,错误为:

错误CS0656:缺乏编译器所需的成员'Microsoft.CSharp.RuntimeBinder.Binder.Convert'

错误CS0656:缺乏编译器所需的成员'Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create'

相关文章
相关标签/搜索