Инкапсулируя действие и func ?
-
28-09-2019 - |
Вопрос
Я пытаюсь сделать дизайн для своего рода 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();
}
До сих пор каждая из моих конкретных действий должна быть ребенком из-за базы DoachWithResultBase или ActionWithoutResult, но мне это действительно не нравится. Если бы я мог переместить определение Execute для ActionBase, учитывая, что конкретный класс может или не может вернуть ценность, я достигнут моей цели.
Кто-то сказал мне, что это может быть сделано с помощью функций и действий, для которых я полностью согласен, но я не могу найти способ иметь это в одном классе, чтобы абонент был бы, если действие будет возвращать значение или нет.
Краткое: я хочу сделать что-то вроде:
// Action1.Execute() returns something.
var a = new Action1();
var result = a.Execute();
// Action2.Execute() returns nothing.
var b = new Action2();
b.Execute();
Решение
Если вы хотите легкое решение, то самый простой вариант будет написать два бетонных класса. Один будет содержать свойство типа 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
Свойство чтение / запись, то вы можете передать делегат действий в качестве аргумента конструктора и сделать свойство 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; }
Другие советы
Следующие интерфейсы должны сделать трюк - это по сути, копирование оттудаваемого шаблона
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() { //... }
}
Вы знаете, что вы можете игнорировать возвращаемое значение метода правильно? Вы не имеют использовать его.
Как насчет чего-то простого:
public class ActionExecuter
{
private MulticastDelegate del;
public ActionExecuter(MulticastDelegate del)
{
this.del = del;
}
public object Execute(params object[] p)
{
return del.DynamicInvoke(p);
}
}