为了账号安全,请及时绑定邮箱和手机立即绑定

在CLR中使用“as”关键字对VS进行强制转换

在CLR中使用“as”关键字对VS进行强制转换

呼唤远方 2019-06-01 11:39:33
在CLR中使用“as”关键字对VS进行强制转换在编程接口时,我发现我正在进行大量的转换或对象类型转换。这两种转换方法有区别吗?如果是的话,是否存在成本差异,或者这对我的项目有何影响?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);     }}此外,什么是“一般”的首选方法?
查看完整描述

4 回答

?
宝慕林4294392

TA贡献2021条经验 获得超8个赞

下面的答案是在2008年写的。

C#7引入了模式匹配,这在很大程度上取代了as运算符,正如您现在可以写的那样:

if (randomObject is TargetType tt){
    // Use tt here}

请注意tt在此之后仍在范围内,但没有明确分配。(它绝对分配在if)在某些情况下,这有点烦人,所以如果您真的关心在每个作用域中引入尽可能少的变量,您可能仍然希望使用is然后是演员。


到目前为止,我认为没有任何答案(在开始回答的时候!)已经解释了什么地方值得使用。

  • 别这么做:

    // Bad code - checks type twice for no reasonif (randomObject is TargetType){
        TargetType foo = (TargetType) randomObject;
        // Do something with foo}

    这不仅是检查两次,而且可能是检查不同的东西,如果randomObject是字段而不是局部变量。如果另一个线程更改randomObject在两者之间。

  • 如果randomObject真的是.的一个实例TargetType也就是说,如果不是,这意味着有一个错误,那么转换是正确的解决方案。这会立即引发异常,这意味着在不正确的假设下不会做更多的工作,并且异常正确地显示了错误的类型。

    // This will throw an exception if randomObject is non-null and// refers to an object of an incompatible type. The
     cast is
    // the best code if that's the behaviour you want.TargetType convertedRandomObject = (TargetType) randomObject;
  • 如果randomObject 强权是.的一个实例TargetTypeTargetType是引用类型,然后使用如下代码:

    TargetType convertedRandomObject = randomObject as TargetType;if (convertedRandomObject != null){
        // Do stuff with convertedRandomObject}
  • 如果randomObject 强权是.的一个实例TargetTypeTargetType是一个值类型,那么我们就不能使用as带着TargetType本身,但我们可以使用一个可空类型:

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

    (注:目前这是实际上比is+强制转换慢..我认为它更优雅、更连贯,但我们开始了。)

  • 如果你真的不需要转换值,但你只需要知道它是否一个TargetType的实例,然后是is接线员是你的朋友。在这种情况下,TargetType是引用类型还是值类型并不重要。

  • 可能还有其他涉及泛型的情况is是有用的(因为您可能不知道T是否是引用类型,因此不能用作引用类型),但它们相对比较模糊。

  • 我几乎肯定用过is对于以前的值类型情况,没有考虑使用可空类型和as共:)


编辑:请注意,除了Value类型的情况之外,上面没有提到性能问题,因为我已经注意到,对可为空的值类型取消装箱实际上要慢一些-但是一致的。

根据NAASK的回答,使用现代的JIT进行即时广播或空检查都是一样快的,如下代码所示:

using System;using System.Diagnostics;using System.Linq;class Test{
    const int Size = 30000000;

    static void Main()
    {
        object[] values = new object[Size];
        for (int i = 0; i < Size - 2; i += 3)
        {
            values[i] = null;
            values[i + 1] = "x";
            values[i + 2] = new object();
        }
        FindLengthWithIsAndCast(values);
        FindLengthWithIsAndAs(values);
        FindLengthWithAsAndNullCheck(values);
    }

    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);
    }}

在我的笔记本电脑上,这些都在大约60毫秒内执行。有两件事要注意:

  • 他们之间没有明显的差别。(事实上,在某些情况下,as-plus-null检查是肯定的。

    慢点。上面的代码实际上使类型检查变得容易了,因为它是针对一个密封类的;如果您正在检查一个接口,那么余额会稍微倾向于as-plus-null检查。)
  • 他们都是

    疯狂

    快地。这很简单

    不会

    成为您代码中的瓶颈,除非您真的不打算这样做

    什么都行

    之后的值。

所以我们不用担心演出。让我们担心正确性和一致性。

我认为,在处理变量时,IS和CAST(或IS和AS)都是不安全的,因为它所引用的值的类型可能会因为测试和强制转换之间的另一个线程而改变。这将是一种非常罕见的情况-但我宁愿有一个我可以始终如一地使用的会议。

我还坚持认为,空值检查可以更好地分离关注点。我们有一个尝试转换的语句,然后一个使用结果的语句。is-and cast或is-and-as执行一个测试,并且然后另一次尝试转换值。

换一种说法,会不会有人从未有过写:

int value;if (int.TryParse(text, out value)){
    value = int.Parse(text);
    // Use value}

这就是目前正在做的事情-尽管显然是以一种更便宜的方式。


查看完整回答
反对 回复 2019-06-01
?
慕妹3146593

TA贡献1820条经验 获得超9个赞

“AS”如果不可能进行强制转换,将返回NULL。

铸造前会引发一个例外。

对于性能而言,提出异常通常会花费更多时间。


查看完整回答
反对 回复 2019-06-01
  • 4 回答
  • 0 关注
  • 635 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信