Вы можете достичь того, что ищете, используя System.Linq.Expressions. Почему вы хотите сделать это таким образом, это другое дело. =)
См. Этот другой ответ о том, откуда взялись ключевые детали использования Expression
.
Обратите внимание, что ваш код для создания var funcType
не возвращался typeof(Func<Square>)
, но вместо typeof(Func<Lazy<Square>>)
; Я исправил это. Большинство вещей было сделано public
для удобства при компиляции. Вы можете изменить доступ к GetShape, если хотите обновить BindingFlags, например, при вызове GetMethod.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace SomeNamespace
{
class Program
{
static void Main(string[] args)
{
ShapeTools st = new ShapeTools();
ShapeTools.Square s = st.aLazyShape.Value;
// s will be whatever ShapeTools.GetShape returns
}
public class ShapeTools
{
public abstract class Shape { }
public class Square : Shape { }
public Lazy<Square> aLazyShape;
public ShapeTools()
{
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented, just an example)
public static object GetShape(Type shapeType) { return new Square(); }
void setup(FieldInfo field)
{ // only handles 'aLazyShape' right now
Type funcType = typeof(Func<>).MakeGenericType(field.FieldType.GenericTypeArguments[0]); // = typeof(Func<Square>)
Type shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// get MethodInfo for the static method in this class that returns the right shape
MethodInfo getInstanceOfType = GetType().GetMethod(nameof(GetShape));
// build the Func dynamically
var typeConst = Expression.Constant(shapeType); // get the shapeType as an Expression
var callGetInstance = Expression.Call(getInstanceOfType, typeConst); // invoke our (static) method to get the instance of shape
var cast = Expression.Convert(callGetInstance, shapeType); // cast the return of our method (which is object) to the right type
var toLambda = Expression.Lambda(cast); // wrap everything in a Lambda to return our instance
var finalFunc = toLambda.Compile(); // compile to the final Func
var lazy = Activator.CreateInstance(field.FieldType, finalFunc); // now create the Lazy<T>, where T is Square
field.SetValue(this, lazy);
}
}
}
}
Наконец, обратите внимание, что GetShape
был сделан stati c. Это было для удобства при использовании Expressions - если вы хотите, вы можете вместо этого передать экземпляр ShapeTools в код Expressions.
И, как написано, ShapeTools.setup
является просто примером того, как это может работать. Я предполагаю, что вы захотите очистить его для обработки полей других типов, кроме Lazy<Shape>
.