Инкапсулирующее действие <T>и Func <T>? - PullRequest
11 голосов
/ 25 ноября 2010

Я пытаюсь сделать дизайн для своего рода интерфейса 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();

Ответы [ 4 ]

9 голосов
/ 25 ноября 2010

Если вы хотите облегченное решение, то самым простым вариантом будет написать два конкретных класса.Один будет содержать свойство типа 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; }
2 голосов
/ 25 ноября 2010

Следующие интерфейсы должны сделать свое дело - это по сути копирование шаблона 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() { //... }
}
0 голосов
/ 25 ноября 2010

как насчет чего-то простого:

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

    public object Execute(params object[] p)
    {
        return del.DynamicInvoke(p);
    }
}
0 голосов
/ 25 ноября 2010

Вы знаете, что можете игнорировать возвращаемое значение метода, верно? Вам не нужно использовать .

...