【问题标题】:What passable value can I use, besides an object instance, to access multiple functions?除了对象实例之外,我还可以使用什么可传递的值来访问多个函数?
【发布时间】:2015-10-06 16:59:24
【问题描述】:

我正在学习 C#,我正在寻找可能由不良架构引起的问题的解决方案,并且不会影响实际程序。

我想传递包含多个函数且不需要实例化的“某物”(不一定是类),例如,我想在字典中有一个包含小时和任务列表的类,所以,例如,在 12:00 我希望班级“午餐”,但午餐可能取决于其他变量,所以我有一个要检查的字典条目,例如 {12,LunchTask},LunchTask 是 ' 的子类/实现/派生Task',这样我们就可以安全地传递它并调用 SomeTask.Start、SomeTask.Pause、SomeTask.Stop 之类的东西。

我虽然关于使用 Dictionary (int,System.Type) 但无法使其正常工作,我也尝试过静态,但它们不能被子类化,并且据我所知,委托适用于单个函数。我只想在一个 dict 中传递一些东西,它具有无需实例化即可直接访问的功能。我知道一种可行但我觉得非常不雅的解决方案是拥有一个包含所有不同任务实例的静态类。

我不知道有什么更好的方法来实现这样的基本功能,也许我做的一切都非常错误。因此,如果你们能指出我正确的方向,我将不胜感激。提前谢谢你。

这是一些(伪 C#)代码:

public abstract class Task {
    public abstract void ExecuteTask ();
    public virtual void PauseTask() {Console.WriteLine ("Task Paused")}
    public virtual void StopTask() {Console.WriteLine ("Task Stopped")}
}

public class Lunch : Task {
    public override void ExecuteTask ()
    {
        Console.WriteLine ("Lunch Task Started");
    }
}

//the following is gonna be instantiated
public class Human {
    Dictionary<int, Something> attributions =  new Dictionary<int, Something>(){{12, Lunch}};
    void ToBeCalledEveryHour () {
        int hour = someHour();
        if (attributions.ContainsKey(hour))
            attributions[hour].ExecuteTask();
    }
}

【问题讨论】:

  • 如果你不展示任何代码,或者清楚地描述你想要达到的目标,几乎不可能说出你想要什么。听起来您 可能 正在尝试创建 Dictionary&lt;int, Task&gt;Dictionary&lt;int, Func&lt;Task&gt;&gt; 或其他 bcl 委托类型之一,但就目前而言,很难说出您在问什么。
  • @RonBeyer:谢谢,我去看看。 PrestonGuillot:对不起,我要编辑问题并添加一些代码。第一个选项需要一个实例 afaik,我不确定第二个,我要去谷歌搜索它。
  • @PrestonGuillot:我刚刚添加了一些代码,它既不漂亮也不工作,但我希望它能达到目的。

标签: c# oop dictionary architecture


【解决方案1】:

如果你的方法的参数总是相同的,那就很简单了。 但是,如果它们要不同,这将是非常困难的。

我会说,与仅在 Dispatcher 之外实例化您想要的对象并在其内部调用方法相比,我认为这并没有太大优势。

但这是一个非常基本的示例,说明您要实现的目标(根据您发布的代码进行编辑):

//Method Call:
Dictionary<int, System.Type> attributions = new Dictionary<int, System.Type>(){{12, typeof[Lunch]}};
Dispatcher(attributions);

//Dispatcher
public void Dispatcher(IDictionary<int, System.Type> dictionary) {
    int hour = SomeHour()

    // Instantiate object
    var obj = Activator.CreateInstance(attributions[hour]);

    //Build args
    var args = new object[]{
        new object(),
    };

    // Invoke method
    type.InvokeMember("ExecuteTask", System.Reflection.BindingFlags.Public, null, obj, args);
}

【讨论】:

  • 请记住,您正在尝试将弱类型原则应用于强类型语言。准备好在不匹配的情况下立即抛出异常。
  • 谢谢,但是对于这样一个基本功能来说,这似乎很复杂。也许保持简单的唯一选择是创建一个静态类并在其中实例化所有任务子类,因此我可以在字典值中传递实例,而不是“某事”,我会使用“任务”。我认为这会起作用,但我不知道它是否可以接受。
  • 我感受到了你的痛苦。来自 Perl 背景,这应该只是: Dispatcher($dictionary) $dictionary->{SomeHour()}->ExecuteTask();
  • 谢谢。我最终放弃了整个代码。我以前在 python 中使用过这种方法并取得了巨大的成功。它会更清楚地表示与人员相关的待办事项列表,但似乎在 c# 中传递逻辑块并不像在 python 中那样容易。我总是尝试构建我的程序以密切反映现实生活中的交互,在 python 中我可以轻松做到这一点,但似乎我将在 C# 中尝试不同的方法。我不是在抱怨或说 C# 不好,我只是需要考虑一些不同的事情。
【解决方案2】:

您可以使用类似以下的内容来存储函数字典,其中每个函数都有相同的签名。

然后您可以传递对此类字典的引用。您还可以将对静态 Compute 方法的引用传递给任何接受 Func

的方法
internal enum ArithmeticFunction
{
   Add,
   Subtract,
   Multiply,
   Divide,
   Min,
   Max,
}

internal static class FunctionMap
{
   private static readonly Dictionary<ArithmeticFunction, Func<int, int, int>> s_map;

   static FunctionMap()
   {
      s_map = new Dictionary<ArithmeticFunction, Func<int, int, int>>();

      s_map[ArithmeticFunction.Add] = (x, y) => x + y;
      s_map[ArithmeticFunction.Subtract] = (x, y) => x - y;
      s_map[ArithmeticFunction.Multiply] = (x, y) => x * y;
      s_map[ArithmeticFunction.Divide] = (x, y) => x / y;
      s_map[ArithmeticFunction.Min] = System.Math.Min;
      s_map[ArithmeticFunction.Max] = System.Math.Max;
   }

   //Don't allow callers access to the core map.  Return them a copy instead.
   internal static Dictionary<ArithmeticFunction, Func<int, int, int>> GetCopy()
   {
      return new Dictionary<ArithmeticFunction, Func<int, int, int>>(s_map); 
   }

   internal static int Compute(ArithmeticFunction op, int x, int y)
   {
      return s_map[op](x, y);
   }

}

static void Main(string[] args)
{

   System.Diagnostics.Trace.WriteLine("Example 1:");
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Add, 12, 4));
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Subtract, 12, 4));
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Multiply, 12, 4));
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Divide, 12, 4));
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Min, 12, 4));
   System.Diagnostics.Trace.WriteLine(FunctionMap.Compute(ArithmeticFunction.Max, 12, 4));

   var safeCopy = FunctionMap.GetCopy();
   System.Diagnostics.Trace.WriteLine("Example 2:");
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Add](72, 9));
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Subtract](72, 9));
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Multiply](72, 9));
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Divide](72, 9));
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Min](72, 9));
   System.Diagnostics.Trace.WriteLine(safeCopy[ArithmeticFunction.Max](72, 9));

输出如下:

Example 1:
16
8
48
3
4
12

Example 2:
81
63
648
8
9
72

【讨论】:

    【解决方案3】:

    在您的情况下,您可以使用对象池模式或工厂模式。

    在对象池模式的情况下,您可以有一个映射,其中的键应该是唯一标识对象的字符串,例如它可以是类名,值可以是对应的对象。

    像这样:

    public abstract class Task {
    public abstract void ExecuteTask ();
    public virtual void PauseTask() {Console.WriteLine ("Task Paused")}
    public virtual void StopTask() {Console.WriteLine ("Task Stopped")}
    }
    
    public class Lunch : Task {
       public override void ExecuteTask ()
       {
         Console.WriteLine ("Lunch Task Started");
       }
    }
    
    public class ObjectCollection{
        Dictionary<string,Task> objectStringMapper= new Dictionary<string,string>();
        Dictionary<string,Task> objectTimeMapper= new Dictionary<string,string>();
        public ObjectCollection(){
            objectMapper.Add("Lunch",new LunchTask());
            objectTimeMapper.Add(12,new LunchTask());        
        }
        public Task getObject(string objId){
            return objectMapper.get(objId);
        }
        public Task getObject(int time){
            return objectTimeMapper.get(time);
        } 
    
     }
    
    public class Human {
        ObjetCollection objectsFactory = new ObjectCollection();
        void ToBeCalledEveryHour () {
            int hour = someHour();
            if (attributions.ContainsKey(hour))
                objectsFactory.getObject(hour).ExecuteTask();
          }
    }
    

    或者您可以选择 Factory Pattern ,在其中您可以拥有一个使用反射或 switch case 创建对象的类。

    注意:由于我是 Java 开发人员并且是 c# 新手,您可能会发现某些语法有问题。

    【讨论】:

    • 我接受这个,因为它似乎是我想要的最接近的解决方案,所以为了将来参考,我认为这个答案是最重要的,但是,我最终放弃了该架构并使用另一种方法。
    • 困难是什么? @Alan C.
    • 我只是认为这根本不是一个很好的方法,至少对于 C#,我真的很喜欢 C#,但你不能像我所使用的语言 Python 那样随便输入你的想法最习惯。我认为您的解决方案非常好,我相信它会在未来帮助其他人。谢谢。
    猜你喜欢
    • 2011-04-18
    • 2012-11-27
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 2019-07-16
    相关资源
    最近更新 更多