重载函数中的C#函数指针

时间:2011-05-03 07:02:22

标签: c# overloading

我有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 个答案:

答案 0 :(得分:8)

虽然没有直接回答你的问题,但我在编写测试用例时遇到了以下问题,你可以通过将调用包装在另一个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

  

一个有趣的事实:lambdas的转换规则确实考虑到了   帐户退货类型。如果你说Foo(()=&gt; X())那么我们就是正确的   事情。 lambdas和方法组有不同的事实   可兑换规则是相当不幸的。

答案 1 :(得分:7)

编辑:这是由C#规范的Overload resolution部分中定义的流程引起的。一旦获得适用的候选函数成员集,它就无法选择“最佳函数”,因为它在重载解析期间不会在帐户中采用返回类型。由于它无法选择最佳功能,所以根据规范发生呼叫错误。

但是,如果您的目标是简化方法调用并避免长时间转换为func,则可以使用泛型并使_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);

编译得很好

答案 2 :(得分:4)

您可以使用Func<string, object[], DbCommand>吗?这也可以让你摆脱过载,只需要一个功能。

答案 3 :(得分:1)

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

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);