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


386

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

这两种转换方法之间有区别吗?如果是这样,是否存在成本差异,或者这对我的计划有何影响?

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

另外,“一般”是什么首选方法?


您能否添加一个小例子说明您为什么首先在问题上使用强制类型转换,或者可能重新开始使用?我对您为什么只需要进行单元测试的转换感兴趣。我认为这超出了这个问题的范围。
Erik van Brakel

2
我可能可以更改我的单元测试来防止这种需求。基本上可以归结为以下事实:我在我的具体对象上有一个不在接口中的属性。我需要设置该属性,但现实生活中该属性将通过其他方式设置。这是否回答你的问题?
Frank V

正如帕特里克Hägne机敏下面指出的,IS的差异。
尼尔

Answers:


517

该行下方的答案写于2008年。

C#7引入了模式匹配,它已在很大程度上取代了as运算符,因为您现在可以编写:

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

请注意,tt此后仍在范围内,但未明确分配。(它肯定if体内分配的。)在某些情况下,这有点令人讨厌,因此,如果您真的很想在每个范围内引入尽可能少的变量,则可能仍要使用is强制类型转换。


我认为到目前为止(在开始这个答案之时!),没有任何答案能真正说明在哪里使用该答案。

  • 不要这样做:

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

    这不仅要检查两次,而且还可能要检查其他内容(如果randomObject是字段而不是局部变量)。如果另一个线程更改了randomObject两者之间的值,则“ if”可能会传递,但强制转换失败。

  • 如果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 可能是的实例TargetType并且TargetType是引用类型,则使用如下代码:

    TargetType convertedRandomObject = randomObject as TargetType;
    if (convertedRandomObject != null)
    {
        // Do stuff with convertedRandomObject
    }
  • 如果randomObject 可能是的实例TargetType并且TargetType是值类型,那么我们不能将asTargetType自身使用,但可以使用可为空的类型:

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

    (注意:目前,这实际上比+ cast慢。我认为它更优雅,更一致,但是我们可以了。)

  • 如果您确实不需要转换后的值,但是只需要知道它是否 TargetType的实例,那么is操作员就是您的朋友。在这种情况下,TargetType是引用类型还是值类型都没有关系。

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

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


编辑:请注意,除了值类型的情况外,上述内容均未提及性能,在此情况下,我注意到拆箱为可空值类型实际上较慢-但保持一致。

根据naasking的回答,使用现成的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-check。)
  • 他们都快疯了。除非您以后真的不对这些值做任何事情,否则这根本不会成为代码中的瓶颈。

因此,让我们不必担心性能。让我们担心正确性和一致性。

我认为,在处理变量时,异变(或异变)都是不安全的,因为它所引用的值的类型可能会由于测试和强制转换之间的另一个线程而改变。那将是一种非常罕见的情况-但我宁愿有一个可以一致使用的约定。

我还坚持认为,那么零检查可以更好地分离关注点。我们有一个语句尝试进行转换,然后有一个语句使用结果。按原样或按原样执行测试,然后再次尝试转换该值。

换个方式,会有人写:

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

那是随便做的事情-尽管显然以一种更便宜的方式。


7
这里是成本是/是/铸造在IL方面:atalasoft.com/cs/blogs/stevehawley/archive/2009/01/30/...
底座

3
如果目标对象可能是目标类型,那么为什么使用“ is”和强制转换组合被认为是不好的做法?我的意思是,它生成的代码较慢,但是在这种情况下,其意图要比AS转换更清晰,例如“如果targetObject为targetType,则执行某些操作”,而不是“如果targetObject为null,则执行某些操作”,而且AS子句将创建不必要的变量超出IF范围。
Valera Kolupaev 2010年

2
@Valera:好点,尽管我建议as / null测试足够惯用,几乎所有C#开发人员都应该清楚意图。我个人不喜欢is + cast中的重复项。实际上,我实际上想要一种“假设”构造,它可以同时执行两个操作。他们经常在一起……
乔恩·斯基特

2
@Jon Skeet:对不起,我迟到了。是And Cast:2135,Is And As:2145,As And null检查:1961,specs:OS:Windows 7,CPU:i5-520M,4GB DDR3 1033 ram,阵列基准的128,000,000项。
Behrooz

2
使用C#7,您可以执行:if (randomObject is TargetType convertedRandomObject){ // Do stuff with convertedRandomObject.Value}或使用switch/ case 请参阅文档
WerWet

71

如果无法转换,“ as”将返回NULL。

强制转换将引发异常。

对于性能而言,引发异常通常会花费更多的时间。


3
异常引发更昂贵,但如果你知道该对象可以正确施放,需要,因为安全检查的更多的时间(见安东的反应)。但是,我认为安全检查的成本很小。

17
可能引发异常的代价是要考虑的因素,但它通常是正确的设计。
Jeffrey L Whitledge,2009年

@panesofglass-对于引用类型,将始终在运行时检查as和cast的转换兼容性,因此该因素将不会在两个选项之间进行区分。(如果不是这样,则
强制转换

4
@Frank-例如,如果您需要使用前泛型集合,并且您的API中的方法需要一个Employees列表,而某些小丑则传递一个Products列表,那么无效的强制转换异常可能适合发出信号违反界面要求。
Jeffrey L Whitledge,2009年

27

这是另一个答案,与IL进行了一些比较。考虑一下类:

public class MyClass
{
    public static void Main()
    {
        // Call the 2 methods
    }

    public void DirectCast(Object obj)
    {
        if ( obj is MyClass)
        { 
            MyClass myclass = (MyClass) obj; 
            Console.WriteLine(obj);
        } 
    } 


    public void UsesAs(object obj) 
    { 
        MyClass myclass = obj as MyClass; 
        if (myclass != null) 
        { 
            Console.WriteLine(obj);
        } 
    }
}

现在查看每种方法产生的IL。即使操作码对您没有任何意义,您也可以看到一个主要区别-在DirectCast方法中,首先调用isinst,然后调用castclass。所以基本上是两个电话而不是一个。

.method public hidebysig instance void  DirectCast(object obj) cil managed
{
  // Code size       22 (0x16)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  brfalse.s  IL_0015
  IL_0008:  ldarg.1
  IL_0009:  castclass  MyClass
  IL_000e:  pop
  IL_000f:  ldarg.1
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0015:  ret
} // end of method MyClass::DirectCast

.method public hidebysig instance void  UsesAs(object obj) cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  .locals init (class MyClass V_0)
  IL_0000:  ldarg.1
  IL_0001:  isinst     MyClass
  IL_0006:  stloc.0
  IL_0007:  ldloc.0
  IL_0008:  brfalse.s  IL_0010
  IL_000a:  ldarg.1
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(object)
  IL_0010:  ret
} // end of method MyClass::UsesAs

isinst关键字与castclass

这篇博文比较了两种方法。他的总结是:

  • 直接比较,isinst比castclass更快(尽管只有一点)
  • 当必须执行检查以确保转换成功时,isinst比castclass快得多
  • 不应使用isinst和castclass的组合,因为这比最快的“安全”转换要慢得多(慢了12%以上)

我个人总是使用As,因为它易于阅读,并且是.NET开发团队(还是Jeffrey Richter)推荐的。


我一直在寻找有关强制转换为as的明确解释,此答案使其变得更加清晰,因为它涉及常见的中间语言逐步说明。谢谢!
莫尔斯

18

两者之间最细微的区别之一是,当涉及到强制转换运算符时,不能将“ as”关键字用于强制转换:

public class Foo
{
    public string Value;

    public static explicit operator string(Foo f)
    {
        return f.Value;
    }

}

public class Example
{
    public void Convert()
    {
        var f = new Foo();
        f.Value = "abc";

        string cast = (string)f;
        string tryCast = f as string;
    }
}

这不会在最后一行编译(尽管我认为它在以前的版本中),因为“ as”关键字没有考虑强制转换运算符。这条线string cast = (string)f;工作正常。


12

作为从未抛出异常,如果它不能执行转换返回代替(仅引用类型进行操作)。所以使用as基本上等同于

_myCls2 = _myObj is MyClass ? (MyClass)_myObj : null;

另一方面,如果无法进行转换,则C样式转换会引发异常。


4
等效,是的,但不相同。这样会生成更多的代码。
底座

10

这并不是您问题的真正答案,但我认为这是一个重要的相关问题。

如果要对接口进行编程,则不需要强制转换。希望这些演员很少见。如果不是这样,您可能需要重新考虑一些界面。


到目前为止,我的单元测试最需要铸造,但感谢您提出来。在进行此工作时,我会牢记这一点。
Frank V

同意蟾蜍,我也很好奇为什么单元测试方面与您@Frank V的铸造相关。在需要铸造的地方,通常需要重新设计或重构,因为这表明您正在尝试拔出不同的问题,应该对它们进行不同的处理。
参议员

@TheSenator这个问题已经超过3年了,所以我真的不记得了。但是即使在进行单元测试时,我也可能会积极使用这些接口。可能是因为我使用的是工厂模式,并且无法访问要测试的目标对象上的公共构造函数。
Frank V

9

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

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

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

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

这是一个微优化,不起作用。我进行了一些真实的测试,测试和投射实际上比强制转换和空比较要快,而且它也更安全,因为如果在强制转换的范围之外,您不可能在范围内使用空引用失败。

如果您想知道试播法更快或更慢的原因,则有一个简单而复杂的原因。

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

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

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


7
@naasking:如果您测试了两次(按照您的第一个代码片段),那么如果是字段或ref参数,则类型有可能在两次测试之间改变。这对于局部变量是安全的,但对于字段而言则是安全的。我希望运行您的基准测试,但是您在博客文章中提供的代码并不完整。我同意不进行微优化,但是我不认为两次使用该值比使用“ as”和一个无效性测试更具可读性或雅致性。(顺便说一句,我肯定会使用直投而不是“ as”,顺便说一句。)
Jon Skeet 2010年

5
我也看不出为什么它更安全。实际上,我已经说明了为什么它不太安全。当然,您最终会获得范围为null的变量,但是除非您在随后的“ if”块的范围之外开始使用该变量,否则就可以了。我提出的安全问题(围绕更改其值的字段)是所显示代码的真正问题-您的安全问题要求开发人员不熟悉其他代码。
乔恩·斯基特

1
请注意,+ 1指出实际/播放还是按原样播放并不慢。已经运行一个完整的测试我自己,我可以证实它没有什么区别,只要我能看到-坦率地说,你可以运行一个令人难以置信的在很小的时候铸件的数量。将使用完整代码更新我的答案。
乔恩·斯基特

1
的确,如果绑定不是本地绑定,则有可能出现TOCTTOU错误(检查时间到使用时间),所以这里很重要。至于为什么它更安全,我与许多初级开发人员合作,他们出于某种原因喜欢重用本地人。因此,在我的经验中,强制转换和空值是非常真实的危险,而且因为我没有那样设计代码,所以我从未遇到过TOCTTOU的情况。至于运行时测试速度,它甚至比虚拟分派[1]还要快!回复:代码,我看看是否可以找到强制转换测试的源。[1] Higherlogics.blogspot.com/2008/10/…– naasking 2010
6

1
@naasking:我从未遇到过本地重用问题-但我想说,比起更细微的TOCTTOU错误,在代码审查中更容易发现。还值得指出的是,我只是针对接口而不是密封的类重新运行了自己的基准测试,并且提示了性能,而推荐使用as-then-null-check ...但是正如我所说的,性能是这就是为什么我会在这里选择任何特定的方法。
乔恩·斯基特

4

如果强制转换失败,则'as'关键字不会引发异常。而是将变量设置为null(或将其设置为值类型的默认值)。


3
值类型没有默认值。由于不能用于强制转换值类型。
PatrikHägne,2009年

2
“ as”关键字实际上不适用于值类型,因此它始终设置为null。
Erik van Brakel

4

这不是问题的答案,而是对问题的代码示例的注释:

通常,您不必将对象从IMyInterface强制转换为MyClass。关于接口的伟大之处在于,如果您将对象作为实现接口的输入,则不必关心要获得哪种对象。

如果将IMyInterface强制转换为MyClass,则已经假设您已经获得了MyClass类型的对象,并且使用IMyInterface毫无意义,因为如果将代码与实现IMyInterface的其他类一起提供,则会破坏代码...

现在,我的建议是:如果您的界面设计合理,则可以避免很多类型转换。


3

as运算符只能用于引用类型,不能重载,null如果操作失败,它将返回。它永远不会抛出异常。

强制转换可以在任何兼容的类型上使用,可以重载,如果操作失败,它将抛出异常。

使用哪种选择取决于情况。首先,这取决于是否要在转换失败时引发异常。


1
“ as”还可以用于可为空的值类型,这提供了一种有趣的模式。见我的答案代码。
乔恩·斯基特

1

我的答案只是关于速度,如果我们不检查类型并且在转换后不检查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

不要像我一样专注于速度,因为所有这些都非常快。


同样,在测试中,我发现as转换(不进行错误检查)比强制转换快1-3%(大约540毫秒,而1亿次迭代为550毫秒)。无论是制作还是破坏您的应用程序。
palswim'2

1

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

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将。


1

如果使用针对.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'


0

as关键字的工作方式相同兼容的引用类型之间有明确的转换与如果转换失败,则不引发异常的主要区别。相反,它在目标变量中产生一个空值。由于异常在性能方面非常昂贵,因此它被认为是一种更好的转换方法。


不一样,一个调用IL代码中的CastClass,另一个调用IsInst。
詹妮克斯

0

您选择的内容强烈取决于所需的内容。我更喜欢显式强制转换

IMyInterface = (IMyInterface)someobj;

因为如果对象应该是IMyInterface类型,而不是-这肯定是问题。最好尽早获得错误,因为确切的错误将得到解决,而不是解决其副作用。

但是,如果您处理接受object作为参数的方法,则需要在执行任何代码之前检查其确切类型。在这种情况下as将很有用,因此您可以避免InvalidCastException


0

这取决于您是否要在使用“ as”后检查是否为空,还是希望您的应用抛出异常?

我的经验法则是,如果我始终希望变量是我希望使用类型转换时期望的类型。如果变量不能转换为我想要的值,并且我准备使用as处理空值,则将使用as。



0

OP的问题仅限于特定的铸造情况。标题涵盖了更多情况。
这是我目前能想到的所有相关铸造情况的概述:

private class CBase
{
}

private class CInherited : CBase
{
}

private enum EnumTest
{
  zero,
  one,
  two
}

private static void Main (string[] args)
{
  //########## classes ##########
  // object creation, implicit cast to object
  object oBase = new CBase ();
  object oInherited = new CInherited ();

  CBase oBase2 = null;
  CInherited oInherited2 = null;
  bool bCanCast = false;

  // explicit cast using "()"
  oBase2 = (CBase)oBase;    // works
  oBase2 = (CBase)oInherited;    // works
  //oInherited2 = (CInherited)oBase;   System.InvalidCastException
  oInherited2 = (CInherited)oInherited;    // works

  // explicit cast using "as"
  oBase2 = oBase as CBase;
  oBase2 = oInherited as CBase;
  oInherited2 = oBase as CInherited;  // returns null, equals C++/CLI "dynamic_cast"
  oInherited2 = oInherited as CInherited;

  // testing with Type.IsAssignableFrom(), results (of course) equal the results of the cast operations
  bCanCast = typeof (CBase).IsAssignableFrom (oBase.GetType ());    // true
  bCanCast = typeof (CBase).IsAssignableFrom (oInherited.GetType ());    // true
  bCanCast = typeof (CInherited).IsAssignableFrom (oBase.GetType ());    // false
  bCanCast = typeof (CInherited).IsAssignableFrom (oInherited.GetType ());    // true

  //########## value types ##########
  int iValue = 2;
  double dValue = 1.1;
  EnumTest enValue = EnumTest.two;

  // implicit cast, explicit cast using "()"
  int iValue2 = iValue;   // no cast
  double dValue2 = iValue;  // implicit conversion
  EnumTest enValue2 = (EnumTest)iValue;  // conversion by explicit cast. underlying type of EnumTest is int, but explicit cast needed (error CS0266: Cannot implicitly convert type 'int' to 'test01.Program.EnumTest')

  iValue2 = (int)dValue;   // conversion by explicit cast. implicit cast not possible (error CS0266: Cannot implicitly convert type 'double' to 'int')
  dValue2 = dValue;
  enValue2 = (EnumTest)dValue;  // underlying type is int, so "1.1" beomces "1" and then "one"

  iValue2 = (int)enValue;
  dValue2 = (double)enValue;
  enValue2 = enValue;   // no cast

  // explicit cast using "as"
  // iValue2 = iValue as int;   error CS0077: The as operator must be used with a reference type or nullable type
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.