2011-05-03 25 views
11

tengo 2 sobrecargados C# funciona como este:C# función de puntero en función sobrecargada

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

Básicamente uno utilizando OleCommand y el otro SqlCommand para el valor de retorno de la función.

Pero lo feo de esto es que tengo a emitir el puntero de función al tipo correcto a pesar de que me siento el compilador debe ser capaz de resolver sin problemas:

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

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

¿Hay dicen ninguna manera el compilador es más inteligente para que yo no tenga que hacer el tipo de conversión? ¿O hice algo mal?

EDITAR: Se ha añadido una recompensa porque estoy realmente interesado en aprender. Gracias por cualquier consejo.

+0

wow .. todas las respuestas tienen 4 votos cada uno! ¿Cómo puedo decidir? – Jake

+0

C# 4.0 también podría resolver esto para escenarios 'dinámicos', sin embargo, habría un golpe de rendimiento. –

Respuesta

8

Si bien no contestó su pregunta directamente, me encontré con lo siguiente al escribir un caso de prueba, puede hacer que compile envolviendo la llamada en otra lambda. Lo que elimina la conversión explícita a costa de otra llamada al método (al menos eso creo, no han mirado la IL todavía)

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

EDIT- encontré este post by Eric Lippert that answers this question

Un interesante hecho: las reglas de conversión para lambdas toman en tipos de devolución de cuenta. Si dices Foo (() => X()) entonces hacemos lo correcto . El hecho de que las lambdas y los grupos de métodos tengan diferentes reglas de convertibilidad es bastante desafortunado.

+0

buen enlace. Respuestas perfectamente – saus

+0

hecho interesante de hecho. Gracias =) – Jake

+0

@BrandonAGr ¿Cómo premio la recompensa? ¿Lo está marcando como respuesta suficiente? – Jake

4

¿Se puede usar Func<string, object[], DbCommand>? Eso también le permitiría deshacerse de la sobrecarga y solo necesitaría una función.

+0

He revisado mi proyecto anterior y veo que esto puede no ser posible según mi conocimiento (¿limitado?). Esencialmente, cada _Insert es diferente en el procesamiento de datos ANTES de pasar a Func(). En este caso, no puedo (no sé cómo) probar el tipo de devolución antes incluso de ejecutar Func(). – Jake

7

EDIT: Esto es causado por el proceso definido en la sección Overload resolution de C# especificación. Una vez que obtiene el conjunto de miembros de la función candidata aplicable, no puede elegir la "mejor función" porque no toma el tipo de devolución en la cuenta durante la resolución de sobrecarga. Como no puede elegir la mejor función, el método ambiguo de llamada se produce de acuerdo con las especificaciones.

Sin embargo, si su objetivo es simplificar las llamadas de método y evitar a largo casting para func puede utilizar genéricos para y complicar método _insert por un poco como este por ejemplo: método simplificado

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

Aviso llama

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

que compila bien

+0

, estoy en lo cierto, espero que esté satisfecho con mi última edición –

+0

+1 ... te hizo trabajar para eso;) – saus

+0

@Valentin esto probablemente acaba de responder a mi comentario a Joel =) – Jake

1

así, ya que el valor de retorno de hecho no se tiene en cuenta, ¿qué hay de uno de los siguientes: (nota: he escrito un proyecto de prueba con un código ligeramente diferente, por lo que podría haber algún tipo de problema con él, pero esto debería dar la idea ...)

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 */ 
       }; 
     } 
} 

entonces yo era capaz de conseguir cada uno de ellos para compilar sin calidad:

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

Sí, funciona como un encanto. Ya tuve la idea de Brandon's Overloaded(). Gracias por los detalles. – Jake

Cuestiones relacionadas