【问题标题】:Make a function that can take property of Action<T> or Action创建一个可以获取 Action<T> 或 Action 属性的函数
【发布时间】:2020-08-19 11:15:11
【问题描述】:

我有以下两个功能:

private void CallVoidFunction(Action action, object property = null)
{
    //Lots of code duplicated here 
    action.Invoke();
    //and here between teh 2 methods
}

private void CallVoidFunction<T>(Action<T> action, object property = null)
{
    //Lots of code duplicated here 
    action.Invoke((T)property);
    //and here between teh 2 methods
}

有没有办法将这些组合成一个函数,该函数采用 Action 的通用属性? 否则我会有很多重复的代码。我知道我可以将重复的代码组合成函数并调用,但是可以吗?

ActionAction&lt;T&gt; 似乎不是从同一个基础派生的,所以我认为它不能完成?

我正在按照他们派生的接口的思路进行思考,但我看不出任何共同点?

我能想到的最好的方法如下,但我希望有一个更简单的方法。

public class ActionContainer<T>
    {
        public ActionContainer(Action<T> actionT, Action action, object property)
        {
            _actionT = actionT;
            _action = action;
            _property = property;
        }

        public void Invoke()
        {
            if (_property != null)
                _actionT.Invoke((T)_property);
            else
                _action.Invoke();
        }

        private object _property { get; set; }
        private Action<T> _actionT { get; set; }
        private Action _action { get; set; }
    }

【问题讨论】:

  • 您可以将这两个中的第一个的主体简化为:CallVoidFunction&lt;int&gt;(i =&gt; { action(); }, 0); 然后您就不需要重复代码了。
  • @LasseV.Karlsen 你能发个例子吗
  • private void CallVoidFunction(Action action, object property = null) { CallVoidFunction&lt;int&gt;(i =&gt; { action(); }, 0); }
  • 为什么将property 声明为object 然后转换为T
  • 我认为 @Alex CallVoidFunction&lt;T&gt;(Action&lt;T&gt; action, T property = null)action.Invoke(property); 在正文中应该也可以正常工作。

标签: c# generics delegates action


【解决方案1】:

Action&lt;T&gt; 代表一个 void 函数,它接受一个 T 类型的参数,而 Action 代表一个没有参数的 void 函数。

因此,您将需要重载,因为没有函数匹配这两个签名。

但是,您可以使用闭包来避免重复:

private void CallVoidFunction<T>(Action<T> action, T property = default)
    => CallVoidFunction(() => action(property));

private void CallVoidFunction(Action action)
{
    //...
    action();
    //...
}

这会将接受T 的函数包装在一个不带参数的函数中。

您还应该将 property 声明为 T 而不是 object 以强制执行类型安全。


如果property 仅用作action 的参数,另一种方法是完全删除通用版本并让调用者制定闭包。

如果CallVoidFunction 确实需要以某种方式使用property,那么重载是您唯一的选择:

private void CallVoidFunction<T>(Action<T> action, T property = default)
{
    // Do something with property..
    CallVoidFunction(() => action(property));
}

【讨论】:

  • 根据您的语法,我认为您必须使用更新的 C# 版本?
  • 我觉得T property = null应该变成T property = default
  • @Alex 可能是 C#6 中引入的expresion bodied method
【解决方案2】:

这类似于 Johnathan Barclay 的 answer,但相反。您从完全实现通用版本开始。然后可以从泛型版本派生非泛型版本,传递一个虚拟的(object)null 参数。

private void CallVoidFunction<T>(Action<T> action, T property = default)
{
    //...
    action(property);
    //...
}

private void CallVoidFunction(Action action)
    => CallVoidFunction<object>(_ => action(), null);

优点是property 在完整实现中可用,如果您出于某种原因(如日志记录)需要它。

【讨论】:

    【解决方案3】:

    你应该可以像这样从第二个调用第一个,这样你就可以避免代码重复:

    private void CallVoidFunction<T>(Action<T> action, object property = null)
    {
        CallVoidFunction(() => action.Invoke((T)property), property);
    }
    

    【讨论】:

    • 方法的类型参数..无法推断
    • @Alex 你可以为这个错误添加示例代码吗?
    • 一切如你所愿
    • 删除第二个属性,例如:((T)property), property);修复它
    • @Alex it works 给我。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-02
    • 1970-01-01
    • 1970-01-01
    • 2017-07-02
    相关资源
    最近更新 更多