1

I have a scenario where I need to change public static fields during runtime. I understand that I can do it through reflection as below to get set the public static field I want, but it is really slow.

string typeName = "ABC";
string fieldName = "IsA";

Type.GetType(typeName ).GetField(fieldName ).SetValue(null, value);

var value = Type.GetType(typeName ).GetField(fieldName ).GetValue(null);

I would like to know is there any faster way to access such as using Reflection.Emit, Linq.Expression or other methods. As what I know currently most of them only support fields with an instance.

6
  • 1
    Why do you need to use Reflections can't you just set the value directly? Commented Nov 12, 2020 at 7:36
  • 2
    "As what I know currently most of them only support fields with an instance" - that's not true. Commented Nov 12, 2020 at 7:47
  • @MathewHD it is due to performance tends to be slower. Commented Nov 17, 2020 at 0:39
  • @Evk you are right. I found my answer here. codeproject.com/articles/1118828/…. It supports public static fields. Commented Nov 17, 2020 at 0:39
  • @Evk Oops, my bad. What I wanna say is [public static class] and its static field Commented Dec 15, 2020 at 6:29

1 Answer 1

4

You can use expressions for this. You basically have three options:

  1. Reflection. Slow.
  2. Dynamic compiled expression. Fast.
  3. Typed compiled expression. Super fast.

In your case it's a bit tricky to go for typed expressions. I guess we cannot assume that all static properties will be of type string? The second option allows you to easily create a fast setter for any field type.

Note that when compiling expressions, you must maintain a cache for the compiled delegates. The compilation step is very expensive!

UPDATE: Using typed compiled expressions

class Program
{
    public class Foo
    {
        public static string Name;
    }

    public static void Main()
    {
        var delegateCache = new Dictionary<(string, string), Action<string>>();

        var typeName = typeof(Foo).FullName;
        var fieldName = "Name";

        var key = (typeName, fieldName);

        // Caching is crucial!
        if (!delegateCache.TryGetValue(key, out var d))
        {
            d = CreateStaticSetter(typeName, fieldName);
            delegateCache.Add(key, d);
        }

        d.Invoke("new value");

        Console.WriteLine(Foo.Name);
    }

    private static Action<string> CreateStaticSetter(string typeName, string fieldName)
    {
        var type = Type.GetType(typeName) ?? throw new ArgumentException();
        var field = type.GetField(fieldName) ?? throw new ArgumentException();

        var valueExp = Expression.Parameter(field.FieldType, "value");
        var fieldExp = Expression.Field(null, field);
        var assignExp = Expression.Assign(fieldExp, valueExp);

        var expr = Expression.Lambda<Action<string>>(assignExp, valueExp);
        return expr.Compile();
    }
}

Using dynamic compiled expressions

class Program
{
    public class Foo
    {
        public static string Name;
    }

    public static void Main()
    {
        var delegateCache = new Dictionary<(string, string), Delegate>();
        
        var typeName = typeof(Foo).FullName;
        var fieldName = "Name";

        var key = (typeName, fieldName);

        // Caching is crucial!
        if (!delegateCache.TryGetValue(key, out var d))
        {
            d = CreateStaticSetter(typeName, fieldName);
            delegateCache.Add(key, d);
        }

        // For a strongly typed delegate, we would use Invoke() instead.
        d.DynamicInvoke("new value");

        Console.WriteLine(Foo.Name);
    }

    private static Delegate CreateStaticSetter(string typeName, string fieldName)
    {
        var type = Type.GetType(typeName) ?? throw new ArgumentException();
        var field = type.GetField(fieldName) ?? throw new ArgumentException();
        
        var valueExp = Expression.Parameter(field.FieldType, "value");
        var fieldExp = Expression.Field(null, field);
        var assignExp = Expression.Assign(fieldExp, valueExp);

        // TODO: Can be further optimized with a strongly typed delegate.
        var expr = Expression.Lambda(assignExp, valueExp);
        return expr.Compile();
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

This answer might be a little overly specific in the code, the usage of "value" refers back to the question but because there's no clarity as to what its purpose is, it's unclear if this is part of the reflection or explicit with respect to the question itself.
I'd say the answer is as clear as any Expression example you will find. It compiles, it runs, and you can google the rest. Expression trees are not easily explained.
@l33t Could explain how you can implement the third option ("Typed compiled expression")?
@Aae Sure. Added an example using typed compiled expressions. It's less flexible, but should be a few microseconds faster :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.