2012-06-21 10 views
7

Todo, tengo un método que devuelve una lista. Este método se usa para devolver los parámetros de SQL StoredProcedures, Views y Functions dependiendo del nombre. Lo que quiero hacer es crear una lista de objetos y devolver esta lista a la persona que llama. El método está por debajoC# Generics Instantiation

private List<T> GetInputParameters<T>(string spFunViewName) 
{ 
    string strSql = String.Format(
     "SELECT PARAMETER_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.PARAMETERS " + 
     "WHERE SPECIFIC_NAME = '{0}' AND PARAMETER_MODE = 'IN';", 
     spFunViewName); 
    List<string[]> paramInfoList = new List<string[]>(); 
    DataTable paramDt = Utilities.DTFromDB(conn, "InputParmaters", strSql); 
    if (paramDt != null) 
    { 
     Converter<DataRow, string[]> rowConverter = 
      new Converter<DataRow, string[]>(Utilities.RowColConvert); 
     paramInfoList = Utilities.ConvertRowsToList<string[]>(paramDt, rowConverter); 
    } 
    else 
     return null; 

    // Build the input parameter list. 
    List<T> paramList = new List<T>(); 
    foreach (string[] paramInfo in paramInfoList) 
    { 
     T t = new T(paramInfo[NAME], paramInfo[TYPE], Convert.ToInt32(paramInfo[CHARMAXLEN])); 
     columnList.Add(column); 
    } 
    return columnList; 
} 

I claramente no puedo crear una instancia T través new y pasar al constructor, pero debe quedar claro lo que estoy tratando de hacer. ¿Hay alguna manera de hacer lo que quiero sin tres métodos adicionales?

Nota. El problema principal es que la cantidad de parámetros que paso a T puede ser dos O tres.

Gracias por su tiempo.

Editar: Los struct s que uso son las siguientes

public struct Database 
{ 
    public string name { get; set; } 
    public string filename { get; set; } 
    public List<Table> tables { get; set; } 
    public List<StoredProcedure> sps { get; set; } 
    public List<Function> funcs { get; set; } 
    public List<View> views { get; set; } 
    public Database(string name, string filename) 
    { 
     this.name = name; 
     this.filename = filename; 
    } 
} 

protected internal struct StoredProcedure 
{ 
    public string name { get; set; } 
    public List<string[]> parameters { get; set; } 
    public StoredProcedure(string name, List<string[]> parameters) 
    { 
     this.name = name; 
     this.parameters = parameters; 
    } 
} 

protected internal struct Function 
{ 
    public string name { get; set; } 
    public string output { get; set; } 
    public List<string[]> parameters { get; set; } 
    public Function(string name, string output, List<string[]> parameters) 
    { 
     this.name = name; 
     this.output = output; 
     this.parameters = parameters; 
    } 
} 

protected internal struct View 
{ 
    public string name {get; set;} 
    public List<string[]> parameters { get; set; } 
    public View(string name, List<string[]> parameters) 
    { 
     this.name = name; 
     this.parameters = parameters; 
    } 
} 
+0

¿Cuál * es * el tipo de 'T' probable que esté aquí? –

+0

@Jon Skeet, en realidad es una 'struct'. Estoy intentando construir una estructura de árbol reutilizable para mostrar bases de datos, tablas, etc., pero también quiero poder utilizar la información obtenida de este procedimiento nuevamente, así que utilizo un patrón singleton con 'struct StoredProcedure',' struct Function', etc. . Gracias. – MoonKnight

+0

Supongo que '" InputParmaters "' ¿es un error tipográfico? – comecme

Respuesta

6

Utilice la clase Activator para crear T y pasar los parámetros.

Type type = typeof(T); 
var result = (T)Activator.CreateInstance(type, new object[] { yourParameters }); 

utilizado en su fragmento de código:

T t = Activator.CreateInstance(type, colInfo[NAME], colInfo[TYPE], Convert.ToInt32(colInfo[CHARMAXLEN])); 
+0

Increíble, no sabía nada de esto, muchas gracias por su tiempo ... – MoonKnight

+0

@Killercam No hay problema. Sin embargo, como otros han escrito, es mejor evitar esto y utilizar un appproach diseñado diferente. – Aphelion

+0

En esta etapa, no estoy seguro de por qué? ¿Me puedes cantar? Tengo tres tipos, sps, funciones y vistas. el cable de estos tienen parámetros de entrada solamente (sp y views), las funciones tienen ambas ... – MoonKnight

3

no soy ni Haciendo suya ni desmerecer esta técnica, pero se puede usar:

(T) Activator.CreateInstance(typeof(T), colInfo[TYPE], Convert.ToInt32(colInfo[CHARMAXLEN])); 

creo que prefiero tener separada métodos de fábrica.

1

Se podía utilizar la Lista < DbParameter>

Eso es un poco más evidente.

1

Usted podría intentar esto:

var constructor = typeof(T).GetConstructor(typeof(string), typeof(string), typeof(int)); 
constructor.Invoke(colInfo[NAME], colInfo[TYPE], Convert.ToInt32(colInfo[CHARMAXLEN])); 
3

Usted puede utilizar Activator.CreateInstance() como otros mencionados o pasar un delegado Func<string, string, int, T> evitar la sobrecarga de la reflexión.

List<T> GetInputParameters<T>(string spFunViewName, Func<string, string, int, T> itemCreator) 
{ 

    .... 
    List<T> paramList = new List<T>();  
    foreach (string[] paramInfo in paramInfoList)  
    {   
     T t = itemCreator(paramInfo[NAME], paramInfo[TYPE], 
      Convert.ToInt32(paramInfo[CHARMAXLEN]));   
     paramList.Add(t);  
    }  

    return columnList;  
} 
1

Puede crear parámetros genéricos de base de datos (y las conexiones y comandos, etc.) con ADO.NET DbProviderFactories.

El espacio de nombres System.Data.Common ofrece clases para crear casos DbProviderFactory para trabajar con fuentes de datos específicos. Cuando crea una instancia de DbProviderFactory y le pasa información sobre proveedor de datos, DbProviderFactory puede determinar el objeto de conexión fuertemente tipado para devolver basado en la información que se ha proporcionado .

En su código, puede crear un DbProviderFactory y luego llamar al CreateParameter().

string providerName = ConfigurationManager.ConnectionStrings["YourConnectionString"].ProviderName; 
DbProviderFactory factory = DbProviderFactories.GetFactory(providerName); 
DbParameter parameter = factory.CreateParameter(); 
2

que claramente no puedo instansiate T a través de nuevo y pasar al constructor

Como está escrito, no; Sin embargo, se puede si la restringirá el parámetro de tipo para aceptar sólo los tipos con los constructores:

private List<T> GetInputParameters<T>(string spFunViewName) where T : new() 
{ 
    // your code here 
} 

En el ejemplo anterior sería capaz de decir:

T myItem = new T(); 

En su caso específico, parece que espera que cada uno de los tipos genéricos comparta algo en común. Tenga en cuenta también restringir el tipo con una interfaz:

private List<T> GetInputParameters<T>(string spFunViewName) where T : new(), ISomeInterface 
{ 
    // your code here 
} 

Eso le permitiría, después de crear una instancia que se oponga, aplicar los valores de las propiedades en la interfaz:

T myItem = new T(); 

myItem.SomeProperty = somevalue; 
myItem.AnotherProperty = anothervalue; 

Para obtener más información, echa un vistazo Constraints on Type Parameters (C# Programming Guide) en MSDN para obtener más información sobre las restricciones de tipo genérico.

+0

¡Me gusta esto mucho! Pero, ¿el hecho de que los objetos dentro de mis tres estructuras no sean diferentes significa que no puedo usar una interfaz ya que es todo o nada? – MoonKnight

+0

Idealmente, en este caso, cada una de las estructuras (o clases) podría implementar (o heredar) la misma interfaz o clase base, si no otra cosa, solo algo con un método de fábrica. ¿Sería capaz de actualizar su pregunta para incluir las estructuras reales? –

+0

Esto se ha hecho ... – MoonKnight