2012-06-22 22 views
5

Todo, tengo un método que se utiliza actualmente para invocar DLL de tipo de retorno bool, esto funciona muy bien. Este método esDefinición de métodos genéricos

public static bool InvokeDLL(string strDllName, string strNameSpace, 
          string strClassName, string strMethodName, 
          ref object[] parameters, 
          ref string strInformation, 
          bool bWarnings = false) 
{ 
    try 
    { 
     // Check if user has access to requested .dll. 
     if (!File.Exists(Path.GetFullPath(strDllName))) 
     { 
      strInformation = String.Format("Cannot locate file '{0}'!", 
              Path.GetFullPath(strDllName)); 
      return false; 
     } 
     else 
     { 
      // Execute the method from the requested .dll using reflection. 
      Assembly DLL = Assembly.LoadFrom(Path.GetFullPath(strDllName)); 
      Type classType = DLL.GetType(String.Format("{0}.{1}", 
             strNameSpace, strClassName)); 
      if (classType != null) 
      { 
       object classInstance = Activator.CreateInstance(classType); 
       MethodInfo methodInfo = classType.GetMethod(strMethodName); 
       if (methodInfo != null) 
       { 
        object result = null; 
        result = methodInfo.Invoke(classInstance, new object[] { parameters }); 
        return Convert.ToBoolean(result); 
       } 
      } 

      // Invocation failed fatally. 
      strInformation = String.Format("Could not invoke the requested DLL '{0}'! " + 
              "Please insure that you have specified the namespace, class name " + 
              "method and respective parameters correctly!", 
              Path.GetFullPath(strDllName)); 
      return false; 

     } 
    } 
    catch (Exception eX) 
    { 
     strInformation = String.Format("DLL Error: {0}!", eX.Message); 
     if (bWarnings) 
      Utils.ErrMsg(eX.Message); 
     return false; 
    } 
} 

Ahora, quiero extender este método para que pueda recuperar los valores de retorno desde un archivo DLL de cualquier tipo . Planeé hacer esto usando genéricos, pero de inmediato ingresé en territorio desconocido para mí. ¿Cómo devuelvo T cuando se desconoce en el momento de la compilación? He examinado la reflexión, pero no estoy seguro de cómo se usaría en este caso. Tome el primer cheque en el código anterior

public static T InvokeDLL<T>(string strDllName, string strNameSpace, 
          string strClassName, string strMethodName, 
          ref object[] parameters, ref string strInformation, 
          bool bWarnings = false) 
{ 
    try 
    { 
     // Check if user has access to requested .dll. 
     if (!File.Exists(Path.GetFullPath(strDllName))) 
     { 
      strInformation = String.Format("Cannot locate file '{0}'!", 
              Path.GetFullPath(strDllName)); 
      return "WHAT/HOW??"; 
     ... 

¿Cómo puedo lograr lo que quiero, o tendría que acaba de sobrecargar el método?

Muchas gracias por su ayuda.

+3

En su segundo bloque de código, se ve como una condición de error. ¿No sería más apropiado 'lanzar una nueva ArgumentException()'? En cuanto a las otras devoluciones, haz lo que sugiere Heinzi. –

+0

+1 No estoy seguro en este caso. A veces es más claro definir usted mismo la causa del error como Exception.Message a veces puede ser críptico para el usuario. Sin embargo, una vez dicho esto, no estoy seguro de que este sea uno de esos casos. Gracias por su tiempo ... – MoonKnight

+2

Estoy de acuerdo con Jesse: si no logra encontrar ni invocar el método, debe tirar. El usuario debe esperar que un retorno implique el éxito. El mundo administrado usa la excepción para comunicar fallas como esta. No retornos especializados. – payo

Respuesta

6

Reemplazar

return false; 

por

return default(T); 

y

return Convert.ToBoolean(result); 

por

return (T)result; 
+0

Eso es genial, gracias. ¿Sabes dónde puedo encontrar los valores predeterminados de cada tipo? ¿No parece estar en MSDN o en la guía de idiomas? – MoonKnight

+1

@Killercam [Aquí] (http://msdn.microsoft.com/en-us/library/83fhsxwc (v = vs.100) .aspx), los tipos de referencia son siempre 'null'. –

+0

@ AdamHouldsworth muchas gracias por eso. Lo más apreciado ... – MoonKnight

4

Cuando no tiene un valor real de la DLL, obviamente tiene que crear un valor de alguna parte. La única forma que funciona para todos los tipos posibles T es return default(T), que proporciona el valor predeterminado para ese tipo (es decir, 0 para int, null para cualquier tipo de referencia).

Si coloca restricciones de tipo en el parámetro tipo, puede obtener más opciones, pero a expensas de la genérica.

2

me gustaría hacer algo como esto:

public static InvokeDLLResult<T> InvokeDLL<T>(string strDllName, string strNameSpace, 
         string strClassName, string strMethodName, 
         ref object[] parameters, 
         ref string strInformation, 
         bool bWarnings = false) 
    { 
     try 
     { 
      // Check if user has access to requested .dll. 
      if (!File.Exists(Path.GetFullPath(strDllName))) 
      { 
       strInformation = String.Format("Cannot locate file '{0}'!", 
                 Path.GetFullPath(strDllName)); 
       return InvokeDLLResult<T>.Failure; 
      } 
      else 
      { 
       // Execute the method from the requested .dll using reflection. 
       Assembly DLL = Assembly.LoadFrom(Path.GetFullPath(strDllName)); 
       Type classType = DLL.GetType(String.Format("{0}.{1}", strNameSpace, strClassName)); 
       if (classType != null) 
       { 
        object classInstance = Activator.CreateInstance(classType); 
        MethodInfo methodInfo = classType.GetMethod(strMethodName); 
        if (methodInfo != null) 
        { 
         object result = null; 
         result = methodInfo.Invoke(classInstance, new object[] { parameters }); 
         return new InvokeDLLResult<T>(true, (T) Convert.ChangeType(result, methodInfo.ReturnType)); 
        } 
       } 

       // Invocation failed fatally. 
       strInformation = String.Format("Could not invoke the requested DLL '{0}'! " + 
                 "Please insure that you have specified the namespace, class name " + 
                 "method and respective parameters correctly!", 
                 Path.GetFullPath(strDllName)); 
       return InvokeDLLResult<T>.Failure; 

      } 
     } 
     catch (Exception eX) 
     { 
      strInformation = String.Format("DLL Error: {0}!", eX.Message); 
      if (bWarnings) 
       Debug.WriteLine(eX.Message); 
      return InvokeDLLResult<T>.Failure; 
     } 
    } 

    public class InvokeDLLResult<T> 
    { 
     public InvokeDLLResult(bool success, T result) 
     { 
      Success = success; 
      Result = result; 
     } 

     public bool Success { get; private set; } 
     public T Result { get; private set; } 

     public static InvokeDLLResult<T> Failure = new InvokeDLLResult<T>(false, default(T)); 
    } 

O, me gustaría escribir un método TryInvokeDLL, usando un parámetro de salida para devolver el resultado.

+0

Me gusta esto. Saludos ... – MoonKnight

+0

+1, muy elegante. Sin embargo, creo que puedes omitir 'Convert.ChangeType (result, methodInfo.ReturnType)'. Como 'result' ha sido devuelto por el método al que hace referencia' methodInfo', ya debe tener un tipo dinámico de 'methodInfo.ReturnType'. Ah, y agregaría 'strInformation' al objeto de retorno: los parámetros' ref' son feos. ;-) – Heinzi

+0

Tiene razón en ambos aspectos, @Heinzi. – neontapir

Cuestiones relacionadas