2

我打算问一个相当详尽的问题,这也是一种沉思,所以请耐心等待...

我正在尝试为模拟应用程序设计工厂实现。模拟将由不同种类的实体组成,即它在任何方面都不是同质模拟。结果,将有许多非常不同的具体实现,并且只有非常一般的属性将在顶层抽象。

我想做的是通过调用模型上的方法来创建新的模拟实体,该方法使用一系列表示实体参数的命名参数,并让模型推断入站描述的对象类型参数(来自参数的名称以及它们可能出现的顺序)并在适当的派生类上调用工厂方法。

例如,如果我向模型传递一对参数(Param1=5000,Param2="Bacon"),我希望它推断名称 Param1 和 Param2“属于”类“Blob1”并调用共享函数“ getBlob1" 带有命名参数 Param1:=5000, Param2:="Bacon" 而如果我通过模型 (Param1=5000, Param3=50) 它将为 Blob2 调用类似的工厂方法;因为 Param1 和 Param3 按此顺序“属于”Blob2。

我预计有几个问题需要解决:

  • 我是否可以使用字符串参数名称反映可用类型以及如果可能的话如何做到这一点
  • 是否有一种巧妙的方法可以从参数列表的组合属性中进行适当的构造函数推断,或者我是否将不得不“手动”做一些事情。
  • 如果可能的话,我希望模型类能够接受参数作为参数,而不是作为一些键和值的集合,这将要求模型在运行时公开大量参数化方法,而无需我显式编码它们 -大概对于相关命名空间中可用的每个工厂方法都有一个。

我真正要问的是你将如何实施这样一个系统,而不是它是否从根本上是可能的。我对 .NET 反射没有远见或经验,无法自己想办法。希望这将证明一个内容丰富的讨论。

更新

好吧,本质上,我在实现一个简单的案例,一个玩具模型方面已经有所突破。因此,核心逻辑是:

Dim members() As Reflection.MethodInfo = Me.GetType.GetMethods()
    Dim params_enum As IEnumerator(Of String) = params.Keys.GetEnumerator

    While params_enum.MoveNext
        members = Array.FindAll(members, _
                                Function(m As Reflection.MethodInfo) Array.Exists(CType(m, Reflection.MethodInfo).GetParameters(), _
                                                                                  Function(mm As Reflection.ParameterInfo) mm.Name = params_enum.Current))
        If members.Count = 0 Then Return False
    End While
    Try
        CType(members(0), Reflection.MethodInfo).Invoke(Me, params.Values.ToArray())
        Return True
    Catch ex As Exception
        Return False
    End Try

这只是在当前实例上选择一个方法并执行它。我想将其扩展到程序集中所有类型的子集。如果可能的话,我还想这样做,让这些类型通过检查它们自己的属性来决定它们在运行时的参数化初始化要求是什么,即每个标有“ParametrisedInitAttribute”的属性都对应于工厂方法中的一个命名参数。这将要求我让这些类型在运行时公开一个带有 N 个参数的方法。有没有办法构造一个具有任意签名的方法并在运行时将其添加到类实现中,或者类似的方式来做这种事情?

4

2 回答 2

1

这似乎是使用dynamic.NET 4 新功能的好地方——DynamicObject可以重写TryInvokeMember以检查调用方法的名称和参数;您可以使用它来构建工厂方法的字典,这些方法将根据传递的参数动态调用。这是我刚刚制作的一个示例:

动态工厂类:

class DynaTest : DynamicObject
{
    Dictionary<string, Func<object[], object>> builders;

    public DynaTest()
    {
        builders = new Dictionary<string, Func<object[], object>>();
        builders.Add("fur,fangs", args => 
                     new Dog((string)args[0], (int)args[1]));
        builders.Add("fur,claws", args => 
                     new Cat((string)args[0], (string)args[1]));
    }

    public override bool TryInvokeMember(InvokeMemberBinder binder, 
                                         object[] args, out object result)
    {
        string key = String.Join(",", binder.CallInfo.ArgumentNames.ToArray());
        Func<object[], object> builder;
        if (builders.TryGetValue(key, out builder))
        {
            result = builder(args);
            return true;
        }
        else
        {
            result = null;
            return false;
        }
    }
}

我们要构建的类:

class Dog
{
    string _fur;
    int _fangs;
    public Dog(string fur, int fangs)
    {
        _fur = fur;
        _fangs = fangs;
    }
    public void Bark() 
    { 
        Console.WriteLine("Woof! I have " + _fur + " fur and " 
                          + _fangs + " fangs."); 
    }
}

class Cat
{
    string _fur;
    string _claws;
    public Cat(string fur, string claws)
    {
        _fur = fur;
        _claws = claws;
    }
    public void Meow() 
    { 
        Console.WriteLine("Meow! I have " + _fur + " fur and "
                          + _claws + " claws."); 
    }
}

测试代码:

static void Main(string[] args)
{
    dynamic dyn = new DynaTest();
    Dog d = dyn.Build(fur: "rough", fangs: 4);
    d.Bark();
    Cat c = dyn.Build(fur: "shiny", claws: "sharp");
    c.Meow();
}

输出:

Woof! I have rough fur and 4 fangs.
Meow! I have shiny fur and sharp claws.

注意:这是我自己第一次玩dynamic,所以可能存在我不知道的问题。

于 2010-06-13T19:56:17.167 回答
0

使用反射完全可以实现您想要做的事情,并且可以按照您的描述或多或少地直接完成:

  • 遍历所有可用类型。你有不同的选择如何做到这一点。您可以通过公共子类,某个接口选择类型,用属性标记它们,......这取决于您的具体实现什么最适合您。还有一些框架可以帮助你做到这一点,但你必须再次检查你的具体要求。

  • 对于所有这些类型,您可以检索“工厂”,例如,它可能是构造函数。

  • 对于所有这些工厂,您可以查询所需参数及其类型和名称。

  • 现在你必须为这些工厂定义一些排序规则,如果你想构造一个对象,你只需遍历列表并搜索第一个匹配的工厂。

实现应该很简单,但可能有点无聊。对于每个步骤,都有不同的选项可以根据您的要求微调解决方案。

于 2010-06-12T11:10:55.617 回答