.method public hidebysig static void Main() il managed { .entrypoint // Code size 72 (0x48) .maxstack 4 .locals (class MyControl V_0, class ISerializable V_1, bool V_2) IL_0000: newobj instance void MyControl::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: isinst ISerializable IL_000c: brfalse.s IL_003d IL_000e: ldloc.0 IL_000f: castclass ISerializable IL_0014: stloc.1 IL_0015: ldloc.1 IL_0016: callvirt instance bool ISerializable::Save() IL_001b: stloc.2 IL_001c: ldstr "The saving of '{0}' was {1}successful" IL_0021: ldloc.0 IL_0022: call instance class System.String FancyControl::get_data() IL_0027: ldloc.2 IL_0028: brtrue.s IL_0031 IL_002a: ldstr "not " IL_002f: br.s IL_0036 IL_0031: ldstr "" IL_0036: call void [mscorlib]System.Console::WriteLine(class System.String, class System.Object, class System.Object) IL_003b: br.s IL_0047 IL_003d: ldstr "The ISerializable interface is not implemented." IL_0042: call void [mscorlib]System.Console::WriteLine(class System.String) IL_0047: ret } // end of method IsOperator2App::Main
We can make this verification process more efficient using the as operator. The as operator converts between compatible types and takes the following form, where expression is any reference type: -
object = expression as type -
You can think of the as operator as a combination of the is operator and, if the two types in question are compatible, a cast. An important difference between the as operator and the is operator is that the as operator sets the object equal to null instead of returning a Boolean value if expression and type are incompatible. Our example can now be rewritten in the following more efficient manner: -
using System; public class FancyControl { protected string Data; public string data { get { return this.Data; } set { this.Data = value; } } } interface ISerializable { bool Save(); } interface IValidate { bool Validate(); } class MyControl : FancyControl, IValidate { public MyControl() { data = "my grid data"; } public bool Validate() { Console.WriteLine("Validating...{0}", data); return true; } } class AsOperatorApp { public static void Main() { MyControl myControl = new MyControl(); ISerializable ser = myControl as ISerializable; if (null != ser) { bool success = ser.Save(); Console.WriteLine("The saving of '{0}' was {1}successful", myControl.data, (true == success ? "" : "not ")); } else { Console.WriteLine("The ISerializable interface is not implemented."); } } }
Now the verification to ensure a valid cast is done only once, which is obviously much more efficient. At this point, let's revisit the MSIL code to see the impact that using the as operator has had: -
.method public hidebysig static void Main() il managed { .entrypoint // Code size 67 (0x43) .maxstack 4 .locals (class MyControl V_0, class ISerializable V_1, bool V_2) IL_0000: newobj instance void MyControl::.ctor() IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: isinst ISerializable IL_000c: stloc.1 IL_000d: ldloc.1 IL_000e: brfalse.s IL_0038 IL_0010: ldloc.1 IL_0011: callvirt instance bool ISerializable::Save() IL_0016: stloc.2 IL_0017: ldstr "The saving of '{0}' was {1}successful" IL_001c: ldloc.0 IL_001d: call instance class System.String FancyControl::get_data() IL_0022: ldloc.2 IL_0023: brtrue.s IL_002c IL_0025: ldstr "not " IL_002a: br.s IL_0031 IL_002c: ldstr "" IL_0031: call void [mscorlib]System.Console::WriteLine(class System.String, class System.Object, class System.Object) IL_0036: br.s IL_0042 IL_0038: ldstr "The ISerializable interface is not implemented." IL_003d: call void [mscorlib]System.Console::WriteLine(class System.String) IL_0042: ret } // end of method AsOperatorApp::Main