Coder Social home page Coder Social logo

fasterflect's Introduction

Code Shelter

NuGet

Fasterflect is available on NuGet: https://www.nuget.org/packages/fasterflect/

Overview

If you frequently use .NET Reflection in your application, you would immediately be aware of its two big issues: usability of the API as a whole and performance of reflection invocations (e.g. method invocation or member access). Fasterflect addresses the usability issue by providing intuitive & highly flexible extension methods to Type, Object and various metadata classes (such as FieldInfo, MethodInfo, etc.) which not only preserve but also enhance the .NET Reflection metadata lookup capability. The performance issue of reflection invocation is addressed by Fasterflect through the use of lightweight dynamic code generation, which performs anywhere from 5 to many hundreds times faster than native .NET Reflection invocations.

Fasterflect offers 3 major areas of functionality:

  • Querying: Fasterflect allows you to query .NET metadata, such as looking-up types in an assembly, searching for methods matching a partial name, finding all constructors of a type, etc.
  • Accessing: Fasterflect allows you to perform reflective invocations on constructors, indexers, fields, properties and methods for both reference types (including arrays) and struct types. Fasterflect also supports working with ref/out parameters, inferring parameter types to simplify invocations. These are backed by the dynamic code generation mechanism.
  • Services: Built on top of the core API for querying and accessing, Fasterflect includes several extensions at at higher level of abstraction. This includes functionality for deep object cloning, object materialization (construction) without relying on default constructors, various object mapping scenarios, XML serialization, etc.

Besides being simple, Fasterflect is heavily documented and unit-tested, so you'll find it to be very easy to learn and use. Kickstart your reflection skills by downloading Fasterflect today. We recommend that you start by spending a bit of time with the extensive documentation, but the impatient need only write using Fasterflect;. We also recommend looking at the unit tests in case the documentation proves to be insufficient.

To get a feel of what Fasterflect is like, without first having to download anything or spend time reading the documentation, below is some sample code using Fasterflect to construct objects and access members.

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

Copyright

Copyright 2010 Buu Nguyen, Morten Mertner

fasterflect's People

Contributors

buunguyen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fasterflect's Issues

MissingMember exception, on runtime.

I have a class, with a instance field of type IList, I set that property from inside the constructor using the "public static IList Properties(this Type type, params string[] names);" method from PropertyExtensions.cs in fasterflect. When I create some instances of my class, and put them into a List, I then get a missing member exception from fasterflect when I call fasterflects deepclone extension method on the list. For some reason, if I populate a HashSet with the same instances of my class, and deepclone the hashset I do not get the exception.

#enhancement Partial deep-cloning

I have a case where I want deep-cloned objects, but not too deep...

One way to do this would be to have a callback on DeepClone that got called at code-gen time with the name and type of the object that's about to have code generated for it. (Or you could include the whole provenance of the type built during the recursion, so it would have a path of names/types.)
If I return false (by looking at the name of the class or field, or custom attrs on the type etc.), then codegen won't build code that clones below that level.

You could also include a second callback -- if that one is present, then it is called at run-time rather than codegen time -- this way deep clone control could also depend on values, through the cloning will be slower. (If deep cloning doesn't use codegen then there is only the 2nd type of callback.)

Modify/extend objects at runtime

I need modify/extend objects at runtime. For example,

I want to extend an Order object returned by an API, so that the serialized JSON response would go from:

{
	"orderId": "abc",
	"price": 100,
	"orderItems": [
		{
			"orderItemId": "def",
			"price": 55
		},
		{
			"orderItemId": "ghi",
			"price": 45
		}
	]
}

to:

{
	"@id": "https://someurl/api/orders/abc",
	"orderId": "abc",
	"price": 100,
	"orderItems": [
		{
			"@id": "https://someurl/api/orders/abc/order-item/def",
			"orderItemId": "def",
			"price": 55
		},
		{
			"@id": "https://someurl/api/orders/abc/order-item/ghi",
			"orderItemId": "ghi",
			"price": 45
		}
	]
}

I am able to accomplish this using reflection, but was wondering if I can simplify the code with fasterflect.

Essentially, I need to an Id field to OrderItem with a JsonPropertyName attribute with the value "@id". I need to do the same thing to the Order object, plus add a couple more fields I didn't include in the example above, for simplicity.

Can fasterflect do this?

Generic type parameters ignored in cache, wrong method invoked

Hi,

The following test fails:

public class GenericTest
{
    static string Foo<T>() => typeof(T).FullName;

    [Test]
    public void Generic_type_params_respected()
    {
        MethodInfo x = typeof(GenericTest).GetMethod(nameof(Foo), BindingFlags.Static|BindingFlags.NonPublic);

        var gen1 = x.MakeGenericMethod(typeof(int));
        var gen2 = x.MakeGenericMethod(typeof(double));
        
        Assert.AreEqual(typeof(int).FullName, gen1.Call());
        Assert.AreEqual(typeof(double).FullName, gen2.Call());
    }
}
  Expected string length 13 but was 12. Strings differ at index 7.
  Expected: "System.Double"
  But was:  "System.Int32"
  ------------------^

The second call gen2.Call() ends up invoking the first method, with the wrong (int) type parameters.
The problem is reversed if you swap the two assert lines to make the double one come first.

        Assert.AreEqual(typeof(double).FullName, gen2.Call());
        Assert.AreEqual(typeof(int).FullName, gen1.Call());
  Expected string length 12 but was 13. Strings differ at index 7.
  Expected: "System.Int32"
  But was:  "System.Double"
  ------------------^

The problem appears to be in the MethodInvocationEmitter ctor - it passes null as the genericTypes argument to the CallInfo constructor, which means that the generic type arguments of the method are ignored when looking up an appropriate delegate from the BaseEmitter.cache

private MethodInvocationEmitter( Type targetType, Flags bindingFlags, string name, Type[] parameterTypes,
	                                 MemberInfo methodInfo )
        : base(new CallInfo(targetType, null, bindingFlags, MemberTypes.Method, name, parameterTypes, methodInfo, true))
	{
	}

#enhancement: duck typing support

Is there any plan to more generally support duck/structural/extensional typing? -- you already support this in your API for finding c'tors automatically based on names and types.

For example, the following API:
StructurallyImplements( typeOrInstance, list of instances or types...)
would return true if the type or instance on the left supports the instances/classes/interfaces on the right, but using nominal/structural testing only.

interface IID { int ID get; }

// returns true, even though the anon obj doesn't support the named interface
StructurallyImplements( new {ID=3}, IID);  

// returns true because the type of the right "covers" the type on the left
StructurallyImplements( new {ID=3, Name="Joe"}, new {ID=3, Name="Sam", Age=42} );  

// returns false because all types on the right are not supported
StructurallyImplements( new {ID=3, Age="42"}, new {ID=3, Name="Sam"});  

// returns false because all types on the right are not supported
StructurallyImplements( new {ID=3, Name="Joe"}, new {ID=3, Name="Sam"}, IAddress );  

This should be simple to implement given your current API, but wondering if it's useful to include.
Now, as far as creating a fast proxy that will create a concrete instance of a named class from an untyped DTO once compatibility is ensured, I guess that's something you don't want to take on in your lib -- as I recall there are a few of these around, but I haven't looked in a while.

I think you should include a benchmark on .NET 4.7

I have run the test under the latest .net framework 4.7 (just changed the project's target framework, not a single line of code), and the result is pretty astonishing.
In short term, .net's reflection has come a long way compaired it was back in 3.5. For many tests, the buildin reflection is as fast, sometimes even faster than fasterflect's un-cached one. I ran the tests multiple times to be sure. Well done, Microsoft!
There're still some cases that un-cached one is faster, but the ratio has dropped to mostly 2x. The cached one are still pretty fast, through.
I think you should modify the Readme.md, tell people who want to use this project under .net 4.7 or newer, to use the cached version, or not at all.

benchmark.4.7-1.txt
benchmark.4.7-2.txt
benchmark.4.7-3.txt

BTW. My test machine is an i7-4700MQ, DDR3-1600 16GB, Windows 8.1 laptop.

ValueTypeHolder's Value property is internal only

Why is it not possible to set the Value contained in a wrapper?
For structs, it works fine using the Get/Set methods, but for base value types (such as float, int, ...) this doesn't work, as they don't have a "setter" method or something.

Or am I missing something?

Extension Methods

Can you put extension methods in their own namespace? The base Fasterflect namespace has all of these extensions for built in types like Type and object. I want is to return the delegates e.g. MethodInvoker without forcing users to add a using per delegate.

Either way, thank you for the hard work.

Just want to say thank you.

Just want to say thank you.

Your lib is so good, save me lots of trouble.

Just want to say thank you, you help me in a huge way.

Unexpected result when checking if generic type implements its generic type definition

Hello,
please have a look at this fiddle: https://dotnetfiddle.net/NlY70e

Dictionary<string, object?> is the concrete type.
IDictionary<string, object?> is the closed generic type.
IDictionary<,> is the open generic type (generic type definition).

Checking if the concrete type implements either the closed or open generic types returns True as expected.
But checking if the closed generic type implements its open generic type returns False, which is not correct in my opinion. I would have expected it to return True since the closed generic type is essentially an "implementation" of its generic type definition.

Can anyone comment on this?
Thanks

DeepClone cloning static fields

We ran into an issue where the DeepClone it seems to be cloning static fields, is this intentional? I can't see why you would ever clone a static field as all instances will be updated.

Issue seems to be here:

IList<FieldInfo> fields = type.Fields( Flags.StaticInstanceAnyVisibility ).Where( f => !f.IsLiteral && !f.IsCalculated( type ) ).ToList();

Seems like Flags.StaticInstanceAnyVisibility should be Flags.InstanceAnyVisibility instead.

Oversight in MemberGetEmitter when dealing with enums

The relevant code is in MemberGetEmitter (and possibly elsewhere in the codebase):

        if (fieldInfo.DeclaringType.IsEnum)
          this.Generator.ldc_i4((int) fieldInfo.GetValue((object) fieldInfo.DeclaringType)).boxIfValueType(fieldInfo.FieldType);
        else
          this.Generator.ldfld(fieldInfo.IsStatic, fieldInfo).boxIfValueType(fieldInfo.FieldType);

A little known fact of C# is that enums can inherit from shorts, bytes and longs, e.g.

public enum MyEnum : short
{
  Foo = 1,
  Bar = 2
}

Atempting to get a field value for enum that inherits from something other than int32 causes the above code to crash with an InvalidCastException

async Task method how to await

public class Test
{
    public static async Task<string> Add(string name)
    {
        await Task.Delay(1000);
        return "hello" + name;
    }

    public static async Task<int> Count()
    {
        await Task.Delay(1000);
        return 1;
    }
}

 var type = typeof(Test);
object m1 = type.CallMethod("Add", "Job");
object m2 = type.CallMethod("Count");

how to await m1 and m2 ?

Task<string> task1 = m1 as Task<string>;
Task<int> task2 = m2 as Task<int>;
Although this can be done

but how to do like common..

object val = await m1 ?
object val2 = await m2 ?

because i need the object value...

Call generic method with out parameter throws NullReferenceException without Flags.ExactBinding

I added the following method to Host in GenericTest:

public bool TryGet<T1, T2>(T2 obj1, out T1 obj2)
{
    obj2 = default;
    return true;
}

If I DO NOT include the Flags as below then it throws a NullReferenceException in MemberFilter when it attempts to call SubString on the name variable inside if( ignoreParameterModifiers && parameterType.IsByRef ). This is because the parameterType.FullName is null as the parameterType is generic.

[TestMethod]
public void Test_invoke_instance_generic_out()
{
    var target = typeof(Host).CreateInstance();
    var parameters = new object[] {1, null};
    var result = (bool)target.CallMethod(new[] {typeof(string), typeof(int)}, "TryGet",
                new[] {typeof(int), typeof(string).MakeByRefType()}, Flags.ExactBinding | Flags.InstancePublicDeclaredOnly, parameters);
    Assert.IsTrue(result);
    Assert.AreEqual(null, parameters[1]);
}

To work around this current problem, as I've done here, you must specify Flags.ExactBinding which then skips that particular bit of code.

Apologies, for not providing a PR with a failing test - I just had time to figure out a workaround and to then write it here so that somebody might fix in the future (or find the workaround!)

Exception: Unable to cast DateTime to ValueTypeHolder

I created a MethodInvoker for DateTime.ToString(). When I call this as Invoker(DateTime.Now, null), I get this exception:

System.InvalidCastException: 'Unable to cast object of type 'System.DateTime' to type 'Fasterflect.Emitter.ValueTypeHolder'.'

Am I doing something wrong, or is this a bug in Fasterflect?

AOT?

How does this work with .net 7 aot?

Accessing nested fields / properties

Hi,

based on the docs I don't think there is direct support for this case so: what would be the best way to to approach the problem of setting or getting the value of a nested field or property, for example:

obj.SetValue("Level1.Leve2.Level3", value);

where each Level# accessor can be either a Field or a Property

Example for multiple parameters in method call

Sorry, can you add an example of how to call a method with multiple parameters?

How should the objectOfParameters be created?
objectCalledUpon.CallMethod(methodInfo.Name, objectOfParameters);

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.