问题 封装Action 和Func ?


我正在尝试为某种IExecutable接口进行设计。我不会详细介绍,但重点是我有几个需要从基类执行的动作。它们可能采用不同的参数(没什么大不了的),它们可能/可能不会返回值。

到目前为止,这是我的设计:

public abstract class ActionBase
{
    // ... snip ...
}

public abstract class ActionWithResultBase<T>: ActionBase
{
    public abstract T Execute();
}

public abstract class ActionWithoutResultBase: ActionBase
{
    public abstract void Execute();
}

到目前为止,我的每个具体动作都需要来自ActionWithResultBase或ActionWithoutResult基础的孩子,但我真的不喜欢这样。 如果我可以将Execute的定义移动到ActionBase,考虑到具体类可能会或可能不会返回值,我将实现我的目标。

有人告诉我这可以通过使用Func和Action来完成,我完全同意,但我找不到一种方法将它放到一个单独的类中,以便调用者知道该操作是否将返回一个值或不。

简介:我想做的事情如下:

// Action1.Execute() returns something.
var a = new Action1();
var result = a.Execute();

// Action2.Execute() returns nothing.
var b = new Action2();
b.Execute();

7973
2017-11-25 16:41


起源

作为澄清:我的目标是在ActionBase中有一个抽象的Execute并避免创建ActionWithResultBase和ActionWithoutResultBase。 - Alpha


答案:


如果你想要一个轻量级的解决方案,那么最简单的选择就是编写两个具体的类。一个将包含类型的属性 Action 另一种属性 Func<T>

public class ActionWithResult<T> : ActionBase { 
  public Func<T> Action { get; set; } 
}

public class ActionWithoutResult : ActionBase {
  public Action Action { get; set; }
}

然后你可以像这样构造两种类型:

var a1 = new ActionWithResult<int> { 
  CanExecute = true,
  Action = () => { 
    Console.WriteLine("hello!");
    return 10; 
  }
}

如果你不想做 Action 属性读/写,然后您可以将操作委托作为参数传递给构造函数,并使属性只读。

C#需要两个不同的委托来表示函数和动作这一事实非常烦人。人们使用的一种解决方法是定义类型 Unit 代表“没有回报价值”并用它代替 void。然后你的类型就是 Func<T> 你可以使用 Func<Unit> 代替 Action。该 Unit 类型可能如下所示:

public class Unit {
  public static Unit Value { get { return null; } }
}

创建一个 Func<Unit> 价值,你会写:

Func<Unit> f = () => { /* ... */ return Unit.Value; }

8
2017-11-25 16:53



谢谢。我知道在标记你的答案之前我已经等了很久,但我真的很想知道是否有人想出了解决方案。即使你的方法没有完全解决问题,它确实是一个很好的答案,并显示了如何克服这个问题的良好和良好的解决方法。 - Alpha


以下接口应该可以解决这个问题 - 它本质上是复制Nullable模式

public interface IActionBase
{
       bool HasResult { get; }
       void Execute() { }
       object Result { get; }
}

public interface IActionBase<T> : IActionBase
{
       new T Result { get; }
}

public sealed class ActionWithReturnValue<T> : IActionBase<T>
{
       public ActionWithReturnValue(Func<T> action) {  _action = action; }
       private Func<T> _action;

       public bool HasResult { get; private set; }
       object IActionBase.Result { get { return this.Result; } }
       public T Result { get; private set; }
       public void Execute()
       {
            HasResult = false;
            Result = default(T);
            try 
            { 
                 Result = _action();
                 HasResult = true;
             }
            catch
            {
                HasResult = false;
                Result = default(T);   
            }  
       }

}

public sealed class ActionWithoutReturnValue : IActionBase
{
      public bool HasResult { get { return false; } }
      object IActionBase.Result { get { return null; } }
      public void Execute() { //... }
}

2
2017-11-25 18:01





你知道你可以忽略方法的返回值吗?你没有  使用它。


0
2017-11-25 16:45



是的,你是对的,但我不认为这是设计明智的。此外,有人建议我总是返回那些只能表明操作成功的无回报的bool,但我也认为这不正确。如果操作不应该返回值,为什么要强制它呢? - Alpha
@Alpha - 我同意你的观点,如果一个手术不能自然地返回它不应该的东西。返回“true”是不正确的,因为你不应该返回“false”,你应该抛出异常。但是我仍然不理解你的设计目标 - 你的目标似乎是基于语法糖,我不知道你正在做什么,你不能只是让Action和Func来解决。我想你需要解释一下他们的问题是什么? - annakata


简单的事情:

public class ActionExecuter
{
    private MulticastDelegate del;
    public ActionExecuter(MulticastDelegate del)
    {
        this.del = del;
    }

    public object Execute(params object[] p)
    {
        return del.DynamicInvoke(p);
    }
}

0
2017-11-25 16:59



也考虑过类似的东西,但我的问题是我不会在编译时知道返回哪种类型。我总是可以使ActionExecuter <T>并使Execute返回T,但是这种情况是我不需要返回任何东西。 - Alpha