在 运行 时间将方法分配给对象 - 设计模式
Assigning methods to object at run-time - Design Pattern
我在我的 C# 代码中创建了一个架构,它完全符合我的要求,但似乎很难在长期 运行 中维护并且我 希望 我可以指出一种设计模式/更好的架构。
我创建了一个对象 Test
,它再次完美地完成了我所需要的,它具有以下结构:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
private Lazy<object> _test1;
public object Test1 { get { return _test1.Value; } }
private Lazy<object> _test2;
public object Test2 { get { return _test2.Value; } }
public Test()
{
_test1 = new Lazy<object>(() => MethodDictionary["Test1"](this), true);
_test2 = new Lazy<object>(() => MethodDictionary["Test2"](this), true);
}
}
这让我可以做的是,在 运行 时为我的 Test
对象和 2 个属性 Test1
和 Test2
分配一个函数字典将使用加载到其中的函数到 return 值。
实施看起来有点像下面这样:
class Program
{
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1.ToString());
Console.WriteLine(x.Test2.ToString());
Console.ReadKey();
}
private static object TestMethod1(Test t)
{ return t.Var1 + t.Var2; }
private static object TestMethod2(Test t)
{ return t.Var1 - t.Var2; }
}
它工作得很好,并且已证明对大型 Test
对象集非常有效。
我的挑战是,如果我想在我的 Test
class 中添加一个新方法,我需要添加:
private Lazy<object> _myNewMethod;
public object MyNewMethod { get { return _myNewMethod.Value; } }
- 使用要在字典中查找的键更新构造函数
而且,虽然这很简单,但我希望有一个单行加载项(可能是某种形式的自定义对象)或直接从字典中读取属性,而无需在全部.
有什么想法吗?任何帮助都会很棒!!!
谢谢!!!
我不知道你想做什么,但我认为你可以使用像这样更简单的方法:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
}
调用函数很简单:
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(Test.MethodDictionary["Test1"](x).ToString());
Console.WriteLine(Test.MethodDictionary["Test2"](x).ToString());
Console.ReadKey();
}
看图书馆https://github.com/ekonbenefits/impromptu-interface。
有了它并使用 DynamicObject,我编写了示例代码来展示如何简化添加新方法:
public class Methods
{
public Methods()
{
MethodDictionary = new Dictionary<string, Func<ITest, object>>();
LazyObjects = new Dictionary<string, Lazy<object>>();
}
public Dictionary<string, Func<ITest, object>> MethodDictionary { get; private set; }
public Dictionary<string, Lazy<object>> LazyObjects { get; private set; }
}
public class Proxy : DynamicObject
{
Methods _methods;
public Proxy()
{
_methods = new Methods();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _methods.LazyObjects[binder.Name].Value;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_methods.MethodDictionary[binder.Name] = (Func<ITest, object>)value;
_methods.LazyObjects[binder.Name] = new Lazy<object>(() => _methods.MethodDictionary[binder.Name](this.ActLike<ITest>()), true);
return true;
}
}
//now you can add new methods by add single method to interface
public interface ITest
{
object Test1 { get; set; }
object Test2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new Proxy().ActLike<ITest>();
x.Test1 = new Func<ITest, object>((y) => "Test1");
x.Test2 = new Func<ITest, object>((y) => "Test2");
Console.WriteLine(x.Test1);
Console.WriteLine(x.Test2);
}
}
您可以实现所需行为的方法之一是使用类似于微型 IoC 框架的东西进行字段注入,并根据您的特定用例进行调整。
为了让事情变得更简单,减少输入具体的 classes 并使事情类型安全,我们引入了 LazyField
类型:
public class LazyField<T>
{
private static readonly Lazy<T> Default = new Lazy<T>();
private readonly Lazy<T> _lazy;
public LazyField() : this(Default) { }
public LazyField(Lazy<T> lazy)
{
_lazy = lazy;
}
public override string ToString()
{
return _lazy.Value.ToString();
}
public static implicit operator T(LazyField<T> instance)
{
return instance._lazy.Value;
}
}
此外,我们定义了一个抽象基础class,以确保在构建时创建这些字段:
public abstract class AbstractLazyFieldHolder
{
protected AbstractLazyFieldHolder()
{
LazyFields.BuildUp(this); // ensures fields are populated.
}
}
暂时跳过这是如何实现的(在下面进一步解释),这允许通过以下方式定义您的 Test
class:
public class Test : AbstractLazyFieldHolder
{
public double Var1;
public double Var2;
public readonly LazyField<double> Test1;
public readonly LazyField<double> Test2;
}
请注意,这些字段是不可变的,在构造函数中初始化。现在,对于您的用法示例,下面的代码片段显示了 "new way" 这样做的过程:
LazyFields.Configure<Test>()
// We can use a type-safe lambda
.SetProvider(x => x.Test1, inst => inst.Var1 + inst.Var2)
// Or the field name.
.SetProvider("Test2", TestMethod2);
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1);
double test2Val = x.Test2; // type-safe conversion
Console.WriteLine(test2Val);
// Output:
// 50
// -10
下面的class提供了支持配置和注入这些字段值的服务。
public static class LazyFields
{
private static readonly ConcurrentDictionary<Type, IBuildUp> _registry = new ConcurrentDictionary<Type,IBuildUp>();
public interface IConfigureType<T> where T : class
{
IConfigureType<T> SetProvider<FT>(string fieldName, Func<T, FT> provider);
IConfigureType<T> SetProvider<F, FT>(Expression<Func<T, F>> fieldExpression, Func<T, FT> provider) where F : LazyField<FT>;
}
public static void BuildUp(object instance)
{
System.Diagnostics.Debug.Assert(instance != null);
var builder = _registry.GetOrAdd(instance.GetType(), BuildInitializer);
builder.BuildUp(instance);
}
public static IConfigureType<T> Configure<T>() where T : class
{
return (IConfigureType<T>)_registry.GetOrAdd(typeof(T), BuildInitializer);
}
private interface IBuildUp
{
void BuildUp(object instance);
}
private class TypeCfg<T> : IBuildUp, IConfigureType<T> where T : class
{
private readonly List<FieldInfo> _fields;
private readonly Dictionary<string, Action<T>> _initializers;
public TypeCfg()
{
_fields = typeof(T)
.GetFields(BindingFlags.Instance | BindingFlags.Public)
.Where(IsLazyField)
.ToList();
_initializers = _fields.ToDictionary(x => x.Name, BuildDefaultSetter);
}
public IConfigureType<T> SetProvider<FT>(string fieldName, Func<T,FT> provider)
{
var pi = _fields.First(x => x.Name == fieldName);
_initializers[fieldName] = BuildSetter<FT>(pi, provider);
return this;
}
public IConfigureType<T> SetProvider<F,FT>(Expression<Func<T,F>> fieldExpression, Func<T,FT> provider)
where F : LazyField<FT>
{
return SetProvider((fieldExpression.Body as MemberExpression).Member.Name, provider);
}
public void BuildUp(object instance)
{
var typedInstance = (T)instance;
foreach (var initializer in _initializers.Values)
initializer(typedInstance);
}
private bool IsLazyField(FieldInfo fi)
{
return fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(LazyField<>);
}
private Action<T> BuildDefaultSetter(FieldInfo fi)
{
var itemType = fi.FieldType.GetGenericArguments()[0];
var defValue = Activator.CreateInstance(typeof(LazyField<>).MakeGenericType(itemType));
return (inst) => fi.SetValue(inst, defValue);
}
private Action<T> BuildSetter<FT>(FieldInfo fi, Func<T, FT> provider)
{
return (inst) => fi.SetValue(inst, new LazyField<FT>(new Lazy<FT>(() => provider(inst))));
}
}
private static IBuildUp BuildInitializer(Type targetType)
{
return (IBuildUp)Activator.CreateInstance(typeof(TypeCfg<>).MakeGenericType(targetType));
}
}
我在我的 C# 代码中创建了一个架构,它完全符合我的要求,但似乎很难在长期 运行 中维护并且我 希望 我可以指出一种设计模式/更好的架构。
我创建了一个对象 Test
,它再次完美地完成了我所需要的,它具有以下结构:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
private Lazy<object> _test1;
public object Test1 { get { return _test1.Value; } }
private Lazy<object> _test2;
public object Test2 { get { return _test2.Value; } }
public Test()
{
_test1 = new Lazy<object>(() => MethodDictionary["Test1"](this), true);
_test2 = new Lazy<object>(() => MethodDictionary["Test2"](this), true);
}
}
这让我可以做的是,在 运行 时为我的 Test
对象和 2 个属性 Test1
和 Test2
分配一个函数字典将使用加载到其中的函数到 return 值。
实施看起来有点像下面这样:
class Program
{
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1.ToString());
Console.WriteLine(x.Test2.ToString());
Console.ReadKey();
}
private static object TestMethod1(Test t)
{ return t.Var1 + t.Var2; }
private static object TestMethod2(Test t)
{ return t.Var1 - t.Var2; }
}
它工作得很好,并且已证明对大型 Test
对象集非常有效。
我的挑战是,如果我想在我的 Test
class 中添加一个新方法,我需要添加:
private Lazy<object> _myNewMethod;
public object MyNewMethod { get { return _myNewMethod.Value; } }
- 使用要在字典中查找的键更新构造函数
而且,虽然这很简单,但我希望有一个单行加载项(可能是某种形式的自定义对象)或直接从字典中读取属性,而无需在全部.
有什么想法吗?任何帮助都会很棒!!!
谢谢!!!
我不知道你想做什么,但我认为你可以使用像这样更简单的方法:
class Test
{
public static Dictionary<string, Func<Test, object>> MethodDictionary;
public double Var1;
public double Var2;
}
调用函数很简单:
static void Main(string[] args)
{
Dictionary<string, Func<Test, object>> MethodDictionary = new Dictionary<string,Func<Test,object>>();
MethodDictionary.Add("Test1", TestMethod1);
MethodDictionary.Add("Test2", TestMethod2);
Test.MethodDictionary = MethodDictionary;
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(Test.MethodDictionary["Test1"](x).ToString());
Console.WriteLine(Test.MethodDictionary["Test2"](x).ToString());
Console.ReadKey();
}
看图书馆https://github.com/ekonbenefits/impromptu-interface。 有了它并使用 DynamicObject,我编写了示例代码来展示如何简化添加新方法:
public class Methods
{
public Methods()
{
MethodDictionary = new Dictionary<string, Func<ITest, object>>();
LazyObjects = new Dictionary<string, Lazy<object>>();
}
public Dictionary<string, Func<ITest, object>> MethodDictionary { get; private set; }
public Dictionary<string, Lazy<object>> LazyObjects { get; private set; }
}
public class Proxy : DynamicObject
{
Methods _methods;
public Proxy()
{
_methods = new Methods();
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _methods.LazyObjects[binder.Name].Value;
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_methods.MethodDictionary[binder.Name] = (Func<ITest, object>)value;
_methods.LazyObjects[binder.Name] = new Lazy<object>(() => _methods.MethodDictionary[binder.Name](this.ActLike<ITest>()), true);
return true;
}
}
//now you can add new methods by add single method to interface
public interface ITest
{
object Test1 { get; set; }
object Test2 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var x = new Proxy().ActLike<ITest>();
x.Test1 = new Func<ITest, object>((y) => "Test1");
x.Test2 = new Func<ITest, object>((y) => "Test2");
Console.WriteLine(x.Test1);
Console.WriteLine(x.Test2);
}
}
您可以实现所需行为的方法之一是使用类似于微型 IoC 框架的东西进行字段注入,并根据您的特定用例进行调整。
为了让事情变得更简单,减少输入具体的 classes 并使事情类型安全,我们引入了 LazyField
类型:
public class LazyField<T>
{
private static readonly Lazy<T> Default = new Lazy<T>();
private readonly Lazy<T> _lazy;
public LazyField() : this(Default) { }
public LazyField(Lazy<T> lazy)
{
_lazy = lazy;
}
public override string ToString()
{
return _lazy.Value.ToString();
}
public static implicit operator T(LazyField<T> instance)
{
return instance._lazy.Value;
}
}
此外,我们定义了一个抽象基础class,以确保在构建时创建这些字段:
public abstract class AbstractLazyFieldHolder
{
protected AbstractLazyFieldHolder()
{
LazyFields.BuildUp(this); // ensures fields are populated.
}
}
暂时跳过这是如何实现的(在下面进一步解释),这允许通过以下方式定义您的 Test
class:
public class Test : AbstractLazyFieldHolder
{
public double Var1;
public double Var2;
public readonly LazyField<double> Test1;
public readonly LazyField<double> Test2;
}
请注意,这些字段是不可变的,在构造函数中初始化。现在,对于您的用法示例,下面的代码片段显示了 "new way" 这样做的过程:
LazyFields.Configure<Test>()
// We can use a type-safe lambda
.SetProvider(x => x.Test1, inst => inst.Var1 + inst.Var2)
// Or the field name.
.SetProvider("Test2", TestMethod2);
var x = new Test() { Var1 = 20, Var2 = 30 };
Console.WriteLine(x.Test1);
double test2Val = x.Test2; // type-safe conversion
Console.WriteLine(test2Val);
// Output:
// 50
// -10
下面的class提供了支持配置和注入这些字段值的服务。
public static class LazyFields
{
private static readonly ConcurrentDictionary<Type, IBuildUp> _registry = new ConcurrentDictionary<Type,IBuildUp>();
public interface IConfigureType<T> where T : class
{
IConfigureType<T> SetProvider<FT>(string fieldName, Func<T, FT> provider);
IConfigureType<T> SetProvider<F, FT>(Expression<Func<T, F>> fieldExpression, Func<T, FT> provider) where F : LazyField<FT>;
}
public static void BuildUp(object instance)
{
System.Diagnostics.Debug.Assert(instance != null);
var builder = _registry.GetOrAdd(instance.GetType(), BuildInitializer);
builder.BuildUp(instance);
}
public static IConfigureType<T> Configure<T>() where T : class
{
return (IConfigureType<T>)_registry.GetOrAdd(typeof(T), BuildInitializer);
}
private interface IBuildUp
{
void BuildUp(object instance);
}
private class TypeCfg<T> : IBuildUp, IConfigureType<T> where T : class
{
private readonly List<FieldInfo> _fields;
private readonly Dictionary<string, Action<T>> _initializers;
public TypeCfg()
{
_fields = typeof(T)
.GetFields(BindingFlags.Instance | BindingFlags.Public)
.Where(IsLazyField)
.ToList();
_initializers = _fields.ToDictionary(x => x.Name, BuildDefaultSetter);
}
public IConfigureType<T> SetProvider<FT>(string fieldName, Func<T,FT> provider)
{
var pi = _fields.First(x => x.Name == fieldName);
_initializers[fieldName] = BuildSetter<FT>(pi, provider);
return this;
}
public IConfigureType<T> SetProvider<F,FT>(Expression<Func<T,F>> fieldExpression, Func<T,FT> provider)
where F : LazyField<FT>
{
return SetProvider((fieldExpression.Body as MemberExpression).Member.Name, provider);
}
public void BuildUp(object instance)
{
var typedInstance = (T)instance;
foreach (var initializer in _initializers.Values)
initializer(typedInstance);
}
private bool IsLazyField(FieldInfo fi)
{
return fi.FieldType.IsGenericType && fi.FieldType.GetGenericTypeDefinition() == typeof(LazyField<>);
}
private Action<T> BuildDefaultSetter(FieldInfo fi)
{
var itemType = fi.FieldType.GetGenericArguments()[0];
var defValue = Activator.CreateInstance(typeof(LazyField<>).MakeGenericType(itemType));
return (inst) => fi.SetValue(inst, defValue);
}
private Action<T> BuildSetter<FT>(FieldInfo fi, Func<T, FT> provider)
{
return (inst) => fi.SetValue(inst, new LazyField<FT>(new Lazy<FT>(() => provider(inst))));
}
}
private static IBuildUp BuildInitializer(Type targetType)
{
return (IBuildUp)Activator.CreateInstance(typeof(TypeCfg<>).MakeGenericType(targetType));
}
}