Skip to content

2 Minute Guide To Accessing API

Ricardo Diaz edited this page Aug 7, 2017 · 1 revision

As promised, this is as simple as it can be to perform reflection operations.

class Person
{
    private int id;
    private int milesTraveled;
    public int Id
    {
        get { return id; }
        set { id = value; }
    }
    public string Name { get; private set; }
    private static int InstanceCount;

    public Person() : this(0) { }

    public Person(int id) : this(id, string.Empty) { }

    public Person(int id, string name)
    {
        Id = id;
        Name = name;
        InstanceCount++;
    }

    public char this[int index]
    {
        get { return Name[index]; }
    }

    private void Walk(int miles)
    {
        milesTraveled += miles;
    }

    private static void IncreaseInstanceCount()
    {
        InstanceCount++;
    }

    private static int GetInstanceCount()
    {
        return InstanceCount;
    }

    public static void Swap(ref int i, ref int j)
    {
        int tmp = i;
        i = j;
        j = tmp;
    }
}

class Program
{
    static void Main()
    {
        var type = Assembly.GetExecutingAssembly().GetType( "FasterflectSample.Person" );
        ExecuteNormalApi(type);
        ExecuteCacheApi(type);
    }

    private static void ExecuteNormalApi(Type type)
    {
        // Person.InstanceCount should be 0 since no instance is created yet
        AssertTrue((int)type.GetFieldValue("InstanceCount") == 0);
        
        // Invokes the no-arg constructor
        object obj = type.CreateInstance();

        // Double-check if the constructor is invoked successfully or not
        AssertTrue(null != obj);

        // Now, Person.InstanceCount should be 1
        AssertTrue(1 == (int)type.GetFieldValue("InstanceCount"));

        // We can bypass the constructor to change the value of Person.InstanceCount directly
        type.SetFieldValue("InstanceCount", 2);
        AssertTrue(2 == (int)type.GetFieldValue("InstanceCount"));

        // Let's invoke Person.IncreaseCounter() static method to increase the counter
        type.CallMethod("IncreaseInstanceCount");
        AssertTrue(3 == (int)type.GetFieldValue("InstanceCount"));

        // Now, let's retrieve Person.InstanceCount via the static method GetInstanceCount
        AssertTrue(3 == (int)type.CallMethod("GetInstanceCount"));

        // Invoke method receiving ref/out params, we need to put arguments in an array
        var arguments = new object[] { 1, 2 };
        type.CallMethod("Swap", 
            // Parameter types must be set to the appropriate ref type
            new[] { typeof(int).MakeByRefType(), typeof(int).MakeByRefType() },
            arguments);
        AssertTrue(2 == (int)arguments[0]);
        AssertTrue(1 == (int)arguments[1]);

        // Now, invoke the 2-arg constructor.  We don't even have to specify parameter types
        // if we know that the arguments are not null (Fasterflect will call arg[n].GetType() internally).
        obj = type.CreateInstance(1, "Doe");

        // id and name should have been set properly
        AssertTrue(1 == (int)obj.GetFieldValue("id"));
        AssertTrue("Doe" == obj.GetPropertyValue("Name").ToString());

        // Let's use the indexer to retrieve the character at index 1
        AssertTrue('o' == (char)obj.GetIndexer(1));

        // If there's null argument, or when we're unsure whether there's a null argument
        // we must explicitly specify the param type array
        obj = type.CreateInstance( new[] { typeof(int), typeof(string) }, 1, null );

        // id and name should have been set properly
        AssertTrue(1 == (int)obj.GetFieldValue("id"));
        AssertTrue(null == obj.GetPropertyValue("Name"));

        // Now, modify the id
        obj.SetFieldValue("id", 2);
        AssertTrue(2 == (int)obj.GetFieldValue("id"));
        AssertTrue(2 == (int)obj.GetPropertyValue("Id"));

        // We can chain calls
        obj.SetFieldValue("id", 3)
           .SetPropertyValue("Name", "Buu");
        AssertTrue(3 == (int)obj.GetPropertyValue("Id"));
        AssertTrue("Buu" == (string)obj.GetPropertyValue("Name"));
         
        // Map a set of properties from a source to a target
        new { Id = 4, Name = "Nguyen" }.MapProperties( obj );
        AssertTrue(4 == (int)obj.GetPropertyValue("Id"));
        AssertTrue("Nguyen" == (string)obj.GetPropertyValue("Name"));

        // Let's have the folk walk 6 miles
	obj.CallMethod("Walk", 6);
		
        // Double-check the current value of the milesTravelled field
        AssertTrue(6 == (int)obj.GetFieldValue("milesTraveled"));

        // Construct an array of 10 elements for current type
        var arr = type.MakeArrayType().CreateInstance(10);

        // GetValue & set element of array
        obj = type.CreateInstance();
        arr.SetElement(4, obj)
           .SetElement(9, obj);

        AssertTrue(obj == arr.GetElement(4));
        AssertTrue(obj == arr.GetElement(9));
        AssertTrue(null == arr.GetElement(0));
    }

    private static void ExecuteCacheApi(Type type)
    {
        var range = Enumerable.Range(0, 10).ToList();

        // Let's cache the getter for InstanceCount
        StaticMemberGetter count = type.DelegateForGetStaticFieldValue("InstanceCount");

        // Now cache the 2-arg constructor of Person and playaround with the delegate returned
        int currentInstanceCount = (int)count();
        ConstructorInvoker ctor = type.DelegateForCreateInstance(new[] { typeof(int), typeof(string) });
        range.ForEach(i =>
        {
            object obj = ctor(i, "_" + i);
            AssertTrue(++currentInstanceCount == (int)count());
            AssertTrue(i == (int)obj.GetFieldValue("id"));
            AssertTrue("_" + i == obj.GetPropertyValue("Name").ToString());
        });

        // Getter/setter
        MemberSetter nameSetter = type.DelegateForSetPropertyValue("Name");
        MemberGetter nameGetter = type.DelegateForGetPropertyValue("Name");

        object person = ctor(1, "John");
        AssertTrue("John" == (string)nameGetter(person));
        nameSetter(person, "Jane");
        AssertTrue("Jane" == (string)nameGetter(person));

        // Invoke method
        person = type.CreateInstance();
        MethodInvoker walk = type.DelegateForCallMethod("Walk", new[] { typeof(int) });
        range.ForEach(i => walk(person, i));
        AssertTrue(range.Sum() == (int)person.GetFieldValue("milesTraveled"));
        
        // Map properties
        var ano = new { Id = 4, Name = "Doe" };
        var mapper = ano.GetType().DelegateForMap( type );
        mapper(ano, person);
        AssertTrue(4 == (int)person.GetPropertyValue("Id"));
        AssertTrue("Doe" == (string)person.GetPropertyValue("Name"));

    }

    public static void AssertTrue(bool expression)
    {
        if (!expression)
            throw new Exception("Not true");
        Console.WriteLine("Ok!");
    }
}
Clone this wiki locally