Click here to Skip to main content
16,022,131 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a base object TUserDataObject, and lots of subclasses ,UserInfo, CompanyInfo e.g. All classes have a private static field __id__. I will build an expression
C#
Func<int> GetId3<T>()
to get the field's value.
As you can see , if i want to get the field value of any subclass type, i should define a
C#
private Func<int> _idGetter = GetId3<UserInfo>();

If i have many subclasses , more than 2000 , maybe, it need define lots of getter.
Is it possible define a custom Func<TUserDataObject, int> to get any subclass's field value ?

What I have tried:

C#
public static Func<int> GetId3<T>()
{
    var t = typeof(T);
    var f = t.GetField("__id__", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Static);
    var fExp = Expression.Field(null, f);

    var valueExp = Expression.Convert(fExp, typeof(int));

    var lambda = Expression.Lambda<Func<int>>(valueExp);

    return lambda.Compile();
}

C#
private Func<int> _idGetter = <GetId3<UserInfo>();

C#
public class TUserDataObject
{
    private static int __id__ = -1;
}

public class UserInfo : TUserDataObject
{
    private static int __id__ = 1;
}

public class CompanyInfo : TUserDataObject
{
    private static int __id__ = 2;
}


public class UserModel : TUserDataObject
{
    private static int __id__ = 10000;
}




-----update----
Think about this ,

C#
private static readonly ConcurrentDictionary<TUserData, TableInfo> _tableInfos = new();

public bool TryGetTableInfo<TUserData>(out TableInfo<TUserData> tableInfo)
{
    var x = _tableInfos.GetOrAdd(typeOf(TUserData) , ... );
    tableInfo = x as TableInfo<TUserData>;
    ....
    return true;
}



but, we know that, UserMode <---mapping---> TableInfo are small amount, and we can use TableInfo[] instead of ConcurrentDictionary<>. with this scenes, the tableInfo's Index can be set in UserMode, for example, __id__.
so , i need a fast delegate call to get __id__ value in method TryGetTableInfo

public bool TryGetTableInfo<TUserData>(out TableInfo<TUserData> tableInfo)
{
    var x = _getter.Invoke(typeOf(TUserData));
    tableInfo = x as TableInfo<TUserData>;
    ....
    return true;
}
Posted
Updated yesterday
v5

You were close. Rather than return Func<int>, you can simply return int from your GetId method and extract the value inside. You're looking for something like this (I pulled this together in LinqPad):
C#
void Main()
{
	var x = new DoIt().GetId<CompanyInfo>();
	System.Diagnostics.Debug.WriteLine(x);
}

public class DoIt
{
  public int GetId<T>()
  {
    var t = typeof(T);
    var f = t.GetField("__id__", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Static);
    var fExp = Expression.Field(null, f);

    var valueExp = Expression.Convert(fExp, typeof(int));

    var lambda = Expression.Lambda<Func<int>>(valueExp);
	var value = lambda.Compile();
	return value();
  }
}

// You can define other methods, fields, classes and namespaces here
public class TUserDataObject
{
    private static int __id__ = -1;
}

public class UserInfo : TUserDataObject
{
    private static int __id__ = 1;
}

public class CompanyInfo : TUserDataObject
{
    private static int __id__ = 2;
}


public class UserModel : TUserDataObject
{
    private static int __id__ = 10000;
}
 
Share this answer
 
Quote:
Is it possible define a custom Func<TUserDataObject, int> to get any subclass's field value ?
You need to create an expression which will use reflection to get the field value. You're also going to need to pass an instance of your class to the function so that it can determine the correct type to access.
C#
public static Func<TUserDataObject, int> GetIdByInstance()
{
	var p = Expression.Parameter(typeof(TUserDataObject), "p");
	var type = Expression.Call(p, nameof(object.GetType), Array.Empty<Type>());
	var field = Expression.Call(type, nameof(Type.GetField), Array.Empty<Type>(), 
        Expression.Constant("__id__"), 
        Expression.Constant(BindingFlags.NonPublic | BindingFlags.Static));
    
	var fieldValue = Expression.Call(field, nameof(FieldInfo.GetValue), Array.Empty<Type>(), Expression.Constant(null));
	var body = Expression.Convert(fieldValue, typeof(int));
	var lambda = Expression.Lambda<Func<TUserDataObject, int>>(body, p);
	return lambda.Compile();
}
The lambda expression will be equivalent to:
C#
(TUserDataObject p) => (int)p.GetType().GetField("__id__", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
Which then raises the question: why are you using LINQ expressions to build the method? You could just use the lambda method directly with the same effect:
C#
public static Func<TUserDataObject, int> GetIdByInstance()
    => (TUserDataObject p) => (int)p.GetType().GetField("__id__", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);

NB: You'll want to measure the performance impact of this. Reflection is notoriously slow, so if you're changing from a (hopefully cached) method per type to a single method that uses reflection, it will slow things down.

If you're using a relatively-recent version of .NET (7 or later), it may be better to use an interface with a static property to retrieve this information:
Explore static virtual members in C# interfaces | Microsoft Learn[^]
 
Share this answer
 
v2
Comments
goldli88 yesterday    
thanks. your answer is almost close. you can see my update for the example, and what i want is a custom delegate getter
private static func<Type,int> _getter = ?;

...
   var x = _getter.Invoke(typfOf(UserInfo));
   var y = _getter.Invoke(typfOf(CompanyInfo));
   ...
   var z = _getter.Invoke(typfOf(UserModel));
 
Richard Deeming yesterday    
If you're passing in the type, then you can use:
Func<Type, int> GetId() => (Type t) => (int)t.GetField("__id__", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);

But that's still going to use reflection at runtime, so you'll need to measure the performance.

You might be better off using a "generic cache":
private static class TableInfoCache<TUserData>
{
    public static readonly TableInfo<TUserData> Table = ...;
}

public bool TryGetTableInfo<TUserData>(out TableInfo<TUserData> tableInfo)
{
    tableInfo = TableInfoCache<TUserData>.Table;
    return true;
}
goldli88 yesterday    
yes , as you mentioned the reflection would be used, and that is what i am trying to avoid by using expression tree to build a delegate. i am trying to find a way to create a generated Fun<t, int=""> getter.
Richard Deeming yesterday    
With this approach, reflection is always going to be used.

Your current approach will use reflection every time the GetId3 method is called. If you cache the returned delegates, that should be once per type.

Any approach that builds one single delegate that takes an instance of, or the type of, the class will involve reflection every time the method is called. The only way around that would be to have that method use a private cache of delegates returned from your existing method.

But you don't need a new expression / delegate for that - just create a wrapper method:
private static class DelegateCache<T> where T : TUserDataObject
{
    public static Func<int> GetIdMethod = GetId3<T>(); // Your existing method
}

public static int GetId<T>() where T : TUserDataObject
    => DelegateCache<T>.GetIdMethod();
goldli88 yesterday    
the static wrapper is a very perfect method. thanks very much.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900