2010-11-25 2 views
9

Я пытаюсь создать дизайн для какого-то интерфейса IExecutable. Я не буду вдаваться в подробности, но дело в том, что у меня есть несколько действий, которые нужно выполнить из базового класса. Они могут принимать разные параметры (неважно), и они могут/не могут возвращать значение.Инкапсулирующее действие <T> и Func <T>?

До сих пор, это мой дизайн:

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(); 
+0

В качестве пояснения: моя цель состоит в том, чтобы иметь абстрактное выполнение в ActionBase и избегать создания ActionWithResultBase и ActionWithoutResultBase. – Alpha 2010-11-25 17:04:34

ответ

6

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

Тот факт, что 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; } 
+0

Спасибо. Я знаю, что я ждал задолго до того, как пометить ваш ответ, но я действительно хотел узнать, придумал ли кто-нибудь решение. Даже когда ваш подход не совсем решает проблему, это действительно хороший ответ и показывает хорошее и приятное обходное решение о том, как преодолеть эту проблему. – Alpha 2011-05-30 21:24:20

0

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

+0

Да, вы правы в этом, но я не думаю, что это было бы что-то конструктивно.Кроме того, кто-то предложил мне всегда возвращать bool для тех возвратов, которые просто указывали бы, что операция прошла успешно, но я также думаю, что это неверно. Если операция не должна возвращать значение, зачем это нужно? – Alpha 2010-11-25 16:48:20

+0

@Alpha - Я согласен с вами в том, что если операция, естественно, не возвращает что-то, это не должно. Возвращение «истина» неверно, потому что, например, вы не должны возвращать «ложный», вы должны выбросить исключение. Однако я все еще не понимаю вашу цель дизайна - ваша цель, похоже, основана на синтаксическом сахаре, и я не понимаю, что вы делаете, что не можете просто заставить Action и Func решить. Я думаю, вам нужно объяснить, в чем проблема с ними? – annakata 2010-11-25 16:52:05

0

, что о чем-то просто:

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

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

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