【问题标题】:C# function pointer in overloaded function重载函数中的 C# 函数指针
【发布时间】:2011-05-03 07:02:22
【问题描述】:

我有 2 个这样的重载 C# 函数:

private void _Insert(Hashtable hash, string tablename, Func<string, object[], SqlCommand> command)
private void _Insert(Hashtable hash, string tablename, Func<string, object[], OleCommand> command)

基本上一个使用OleCommand,另一个使用SqlCommand作为函数的返回值。

但丑陋的一点是我必须将函数指针转换为正确的类型,即使我觉得编译器应该能够毫无问题地解决它:

class RemoteDatabase
{    
      public SqlCommand GetCommand(string query, object[] values);
}

_Insert(enquiry, "Enquiry", (Func<string, object[], SqlCommand>)(_RemoteDatabase.GetCommand));

有什么方法可以告诉编译器更聪明,这样我就不必进行类型转换了?还是我做错了什么?

编辑: 添加了赏金,因为我真的有兴趣学习。感谢您的建议。

【问题讨论】:

  • 哇..所有答案各有 4 票!我该如何决定?
  • C# 4.0 也可以在 dynamic 场景中解决这个问题,但是,性能会受到影响。

标签: c# overloading


【解决方案1】:

虽然没有直接回答您的问题,但我在编写测试用例时确实遇到了以下问题,您可以通过将调用包装在另一个 lambda 中来编译它。它以另一个方法调用为代价删除了显式转换(至少我认为是这样,还没有看过 IL)

class RemoteDatabase
{
    public int GetCommand(){return 5;}
}

class Program
{

    static void Main(string[] args)
    {
        var rd = new RemoteDatabase();

        // Overloaded(1, rd.GetCommand); // this is a compile error, ambigous

        Overloaded(1, () => rd.GetCommand()); // this compiles and works

        Console.ReadLine();
    }

    static void Overloaded(int paramOne, Func<int> paramFun)
    {
        Console.WriteLine("First {0} {1}", paramOne, paramFun());
    }

    static void Overloaded(int paramOne, Func<string> paramFun)
    {
        Console.WriteLine("Second {0} {1}", paramOne, paramFun());
    }
}

编辑- 我找到了这个post by Eric Lippert that answers this question

一个有趣的事实:lambda 的转换规则确实包含 帐户返回类型。如果你说 Foo(()=>X()) 那么我们做对了 事物。 lambda 和方法组有不同的事实 兑换规则是相当不幸的。

【讨论】:

  • 不错的链接。完美回答
  • 确实很有趣。谢谢=)
  • @BrandonAGr 我如何奖励赏金?将其标记为答案是否足够?
  • FAQ,我认为左边应该有一个蓝色按钮,虽然我从未获得过赏金,但不确定
【解决方案2】:

编辑:这是由 C# 规范的 Overload resolution 部分中定义的进程引起的。一旦它获得了一组适用的候选函数成员,它就不能选择“最佳函数”,因为它在重载解析期间没有在帐户中获取返回类型。由于无法选择最佳功能,因此根据规范会发生模棱两可的方法调用错误。

但是,如果您的目标是简化方法调用并避免长时间转换为 func,您可以使用泛型 for 并使 _Insert 方法复杂化,例如:

public  void Main()
    {
        _Insert(new Hashtable(), "SqlTable", F1);
        _Insert(new Hashtable(), "OleTable", F2);
    }

    private static SqlCommand F1(string name, object[] array)
    {
        return new SqlCommand();
    }

    private static OleDbCommand F2(string name, object[] array)
    {
        return new OleDbCommand();
    }

    private void _Insert<T>(Hashtable hash, string tablename, Func<string, object[], T> command) 
    {
        if (typeof(T) == typeof(SqlCommand)) {
            SqlCommand result = command(null, null) as SqlCommand;
        }
        else if (typeof(T) == typeof(OleDbCommand)) {
            OleDbCommand result = command(null, null) as OleDbCommand;
        }
        else throw new ArgumentOutOfRangeException("command");
    }

注意简化的方法调用

_Insert(new Hashtable(), "OleTable", F1);
_Insert(new Hashtable(), "OleTable", F2);

编译就好了

【讨论】:

  • @Valentin 这可能只是回答了我对乔尔的评论 =)
  • 在 C# 中的某些情况下非常典型地使用泛型功能。显然,不是默认用例。使用这种方式可以看作是基于泛型机制的工厂方法。例如,这是您可以在 C# 2.0 中采用的唯一方法,它不包含 lambda。如果两个类都是 DbCommand,则可以添加 where T: DbCommand 以在编译时获得更多类型安全。
  • 我喜欢它,但是您可以完全摆脱方法主体中特定于类型的代码。否则,您不需要泛型,只需传递字符串或 Type 对象即可。
【解决方案3】:

你能用Func&lt;string, object[], DbCommand&gt;吗?这也可以让你摆脱重载,只需要一个函数。

【讨论】:

  • 我重新访问了我的旧项目,发现根据我的(有限的?)知识,这可能是不可能的。本质上,每个 _Insert 在传入 Func() 之前的数据处理中都是不同的。在这种情况下,我什至无法(不知道如何)在执行 Func() 之前测试返回类型。
【解决方案4】:

好吧,既然确实没有考虑返回值,那么其中一个怎么样:(注意:我编写了一个测试项目,代码略有不同,因此可能存在某种问题,但这应该给出这个想法...)

public class RemoteDatabase
{    
      // changed to private, referenced by CommandWay1
      private SqlCommand GetCommand(string query, object[] values)
      {
          /* GetCommand() code */ 
      }

      public Func<string, object[], SqlCommand> CommandWay1
      {
          get
          {
             return (q,v) => GetCommand(q,v);
          }
      }

      // or, you could remove the above and just have this, 
      // with the code directly in the property
      public Func<string, object[], SqlCommand> CommandWay2
      {
          get
          {
             return
                (q,v) =>
                {
                   /* GetCommand() code */ 
                };
      }
}

然后我能够在不强制转换的情况下编译其中的每一个:

_Insert(enquiry, "Enquiry", (q,v) => _RemoteDatabase.GetCommand(q,v));
_Insert(enquiry, "Enquiry", _RemoteDatabase.CommandWay1);
_Insert(enquiry, "Enquiry", _RemoteDatabase.CommandWay2);

【讨论】:

  • 是的,它就像一个魅力。已经从 Brandon 的 Overloaded() 中得到了这个想法。感谢您提供详细信息。
猜你喜欢
  • 2014-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-06
  • 1970-01-01
  • 2015-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多