Description



Description

Late binding is controlled by a customized binding interface through reflection. The Binder class is designed to provide this functionality. Binder objects are used in overload resolution and argument coercion for dynamic invocation of members at runtime.

Access to information obtained from reflection is controlled at two levels: untrusted code and code with System.Security.Permissions.ReflectionPermission.

Untrusted code is code with no special level of trust (such as code downloaded from the Internet). Such code is allowed to invoke anything that it would have been able to invoke in an early bound way.

System.Security.Permissions.ReflectionPermission controls access to metadata through reflection. If this permission is granted to code, that code has access to all the types in its application domain, assembly, and module. It can access information about public, family, and private members of all types it has access to. Two primary capabilities are granted:

  • The ability to read the metadata for family and private members of any type.

  • The ability to access peer classes in the module and peer modules in the assembly.

[Note: The term "reflection" refers to the ability to obtain information about a System.Object during runtime. The primary means through which this information is accessed is via the System.Type of the object. Reflection allows the programmatic discovery of a type's metadata. The information included in the metadata includes details about the assembly or module in which the type is defined as well as members of the type. Reflection uses this information to provide the following primary services: The primary users of these services are script engines, object viewers, compilers, and object persistence formatters. Through reflection, methods can be bound and invoked at runtime. If more than one member exists for a given member name, overload resolution determines which implementation of that method is invoked by the system. Coercion can occur when a parameter specified for a method call does not match the type specified for the parameter in the method signature. When possible, the binder converts the parameter (coerces it) to the type specified by the method signature. Coercion might not be possible depending on the types involved. To bind to a method, field, or property, typically a list of probable candidates is obtained from the System.Type of a System.Object. That list is then passed to the appropriate method of a Binder instance. Based on the other parameters passed to that method, typically (although not necessarily) one of the members of the list is chosen, and an object that reflects that member is returned. The system supplies a default binder that provides default binding rules. Because binding rules vary among programming languages, it is recommended that each programming language provide a custom implementation of Binder.]

Example

using System;

using System.Reflection;

using System.Globalization;





/// <summary>

/// This is a simple binder that allows callers to use a string for

/// any method that takes one of the base data types (int, double, short,

/// etc)

/// Notice, we punt if there is an overload issue that needs to be resolved.

/// </summary>



public class BinderSample

{

    public static void Main()

    {

        int sum;



        sum = (int)typeof(BinderSample).InvokeMember("Sum",

            BindingFlags.InvokeMethod | BindingFlags.NonPublic |

            BindingFlags.Static,

            new StringBinder(), null, new object[] { "4", "2" });

        Console.WriteLine("Sum ('4','2') => {0}", sum);



        sum = (int)typeof(BinderSample).InvokeMember("Sum",

            BindingFlags.InvokeMethod | BindingFlags.NonPublic |

            BindingFlags.Static,

            null, null, new object[] { 4, 2 });

        Console.WriteLine("Sum (4,2) => {0}",sum);



        try

        {

            Console.WriteLine("Sum (4.0,2.5) => ");

            sum = (int)typeof(BinderSample).InvokeMember("Sum",

                BindingFlags.InvokeMethod | BindingFlags.NonPublic |

                BindingFlags.Static,

                new StringBinder(), null, new object[] { 4.0, 2.5 });

        }

        catch (ArgumentException e)

        {

            Console.WriteLine("Expected error: could not convert arguments.");

            Console.WriteLine(e);

        }

        Console.WriteLine();

        Console.WriteLine();

        Console.WriteLine("Press Enter to continue");

        Console.ReadLine();

    }



    static private int Sum(int value1, int value2)

    {

        return value1 + value2;

    }

}

public class StringBinder : Binder

{

    public override FieldInfo BindToField(BindingFlags bindingAttr,

        FieldInfo[] match, object value,

        CultureInfo culture)

    {

        if (match.Length > 1) throw new AmbiguousMatchException();

        return match[0];

    }



    public override MethodBase BindToMethod(BindingFlags bindingAttr,

        MethodBase[] match, ref object[] args,

        ParameterModifier[] modifiers,

        CultureInfo culture,

        string[] names, out object state)

    {

        state = null;

        if (match.Length > 1) throw new AmbiguousMatchException();

        return match[0];

    }

    public override void ReorderArgumentArray(ref object[] args,

        object state)

    {

        //none needed

    }



    public override MethodBase SelectMethod(BindingFlags bindingAttr,

        MethodBase[] match, Type[] types, ParameterModifier[] modifiers)

    {

        if (match.Length > 1) throw new AmbiguousMatchException();

        return match[0];

    }



    public override PropertyInfo SelectProperty(BindingFlags bindingAttr,

        PropertyInfo[] match, Type returnType, Type[] indexes,

        ParameterModifier[] modifiers)

    {

        if (match.Length > 1) throw new AmbiguousMatchException();

        return match[0];

    }





    public override object ChangeType(object value, Type type,

        CultureInfo culture)

    {

        if (value.GetType() == typeof(string))

        {

            Console.WriteLine("Change '{0}' to type {1}", value, type);

            return Convert.ChangeType(value, type);

        }

        return value;

    }



}


The output is


Change '4' to type System.Int32

Change '2' to type System.Int32

Sum ('4','2') => 6

Sum (4,2) => 6

Sum (4.0,2.5) =>

Expected error: could not convert arguments.

System.ArgumentException: Cannot widen from target type to primitive type.

   at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlag

s invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean

isBinderDefault, Assembly caller, Boolean verifyAccess)

   at System.Reflection.RuntimeMethodInfo.InternalInvoke(Object obj, BindingFlag

s invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean

verifyAccess)

   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke

Attr, Binder binder, Object[] parameters, CultureInfo culture)

   at System.RuntimeType.InvokeMember(String name, BindingFlags invokeAttr, Bind

er binder, Object target, Object[] args, ParameterModifier[] modifiers, CultureI

nfo culture, String[] namedParameters)

   at System.Type.InvokeMember(String name, BindingFlags invokeAttr, Binder bind

er, Object target, Object[] args)

   at BinderSample.Main() in c:\System.Reflection\Binder\Binder.cs:line 34





Press Enter to continue