复杂的.NET工厂设计

发布于 2024-09-05 00:23:48 字数 1895 浏览 10 评论 0原文

我打算问一个相当复杂的问题,这也是这里的一个思考问题,所以请耐心等待......

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

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

例如,如果我向模型传递一对参数(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 个参数的方法。有没有一种方法可以构造具有任意签名的方法并在运行时将其添加到类实现中,或者以类似的方式执行此类操作?

I'm planning to ask a fairly elaborate question that is also something of a musing here, so bear with me...

I'm trying to design a factory implementation for a simulation application. The simulation will consist of different sorts of entities i.e. it is not a homogenous simulation in any respect. As a result, there will be numerous very different concrete implementations and only the very general properties will be abstracted at the top level.

What I'd like to be able to do is create new simulation entities by calling a method on the model with a series of named arguments representing the parameters of the entity, and have the model infer what type of object is being described by the inbound parameters (from the names of the parameters and potentially the sequence they occur in) and call a factory method on the appropriate derived class.

For example, if I pass the model a pair of parameters (Param1=5000, Param2="Bacon") I would like it to infer that the names Param1 and Param2 'belong' to the class "Blob1" and call a shared function "getBlob1" with named parameters Param1:=5000, Param2:="Bacon" whereas if I pass the model (Param1=5000, Param3=50) it would call a similar factory method for Blob2; because Param1 and Param3 in that order 'belong' to Blob2.

I foresee several issues to resolve:

  • Whether or not I can reflect on the available types with string parameter names and how to do this if it's possible
  • Whether or not there's a neat way of doing the appropriate constructor inference from the combinatoric properties of the argument list or whether I'm going to have to bodge something to do it 'by hand'.
  • If possible I'd like the model class to be able to accept parameters as parameters rather than as some collection of keys and values, which would require the model to expose a large number of parametrised methods at runtime without me having to code them explicitly - presumably one for every factory method available in the relevant namespace.

What I'm really asking is how you'd go about implementing such a system, rather than whether or not it's fundamentally possible. I don't have the foresight or experience with .NET reflection to be able to figure out a way by myself. Hopefully this will prove an informative discussion.

UPDATE:

Well, I've had a crack at implementing a simple case, a toy model, essentially. The core logic is thus:

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

This just picks a method on the current instance and executes it. I'd like to extend that to a subset of all the types in the assembly. I'd also like to do this, if possible, by letting those types decide what their parametrised initialisation requirements are at runtime by examining their own properties, i.e. each property marked with 'ParametrisedInitAttribute' corresponds to exactly one named argument in the factory method. This would require that I have these types expose a method with N arguments at runtime. Is there a way to construct a method with an arbitrary signature and add it to the class implementation at runtime, or a similar way of doing this sort of thing?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

临走之时 2024-09-12 00:23:48

这似乎是使用 .NET 4 的新 dynamic 功能的好地方 - 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,因此可能存在问题这个我不知道。

This seems like a good place to use the new dynamic features of .NET 4 - a DynamicObject can override TryInvokeMember to examine the name and arguments a method is invoked with; you can use that to build a dictionary of factory methods that will be called dynamically based on the passed arguments. Here's an example I just cooked up:

The dynamic factory class:

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;
        }
    }
}

The classes we want to build:

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."); 
    }
}

Test code:

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();
}

Output:

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

Note: this is my own first time playing with dynamic, so there might be issues with this I'm unaware of.

死开点丶别碍眼 2024-09-12 00:23:48

您想要做的事情完全可以通过反射实现,并且可以或多或少直接按照您所描述的那样完成:

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

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

    对于

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

    对于

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

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

What you want to do is perfectly possible with Reflection and can be done more or less straight forward as you described it:

  • Iterate over all available types. You have different options how to do that. You can choose types by a common subclass, by a certain interface, mark them with an attribute, ... It depends on your concrete implementation what's best for you. There are also frameworks which help to do that, but again you have to check your concrete requirements.

  • For all these types you can retrieve the "factory", which might be the constructor for example.

  • For all these factories you can query the required parameters with their types and names.

  • Now you have to define some ordering rules for these factories and if you want to construct an object, you just go through the list and search for the first matching factory.

Implementation should be quite simple, but perhaps a bit boring. For each step there are different options to fine tune the solution for your requirements.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文