2012-06-21 15 views
7

Tutti, ho un metodo che restituisce una lista. Questo metodo viene utilizzato per restituire i parametri di SQL StoredProcedures, Views e Functions in base al nome. Quello che voglio fare è creare una lista di oggetti e restituire questa lista al chiamante. Il metodo è al di sottoC# 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; 
} 

ho chiaramente posso non un'istanza di T via new e passare al costruttore, ma dovrebbe essere chiaro ciò che sto cercando di fare. C'è un modo per fare ciò che voglio senza tre metodi aggiuntivi?

Nota. Il problema principale è che il numero di parametri che sto passando a T può essere due o tre.

Grazie per il vostro tempo.

Edit: I struct s che uso sono i seguenti

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

Quale * è * il tipo di 'T' probabile che sia qui? –

+0

@Jon Skeet, in realtà è una 'struct'. Sto tentando di costruire una struttura ad albero riutilizzabile per mostrare database, tabelle ecc. Ma voglio anche essere in grado di usare di nuovo le informazioni ottenute da questa procedura - quindi uso un modello singleton con 'struct StoredProcedure',' struct Function' etc . Grazie. – MoonKnight

+0

Suppongo che "InputParmaters" sia un refuso? – comecme

risposta

6

Utilizzare la classe Activator per creare T e passare i parametri.

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

in uso nel proprio frammento di codice:

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

Incredibile, non lo sapevo affatto, grazie mille per il tuo tempo ... – MoonKnight

+0

@Killercam Nessun problema. Tuttavia, come altri hanno scritto, è meglio evitarlo e utilizzare un appproach dal design diverso. – Aphelion

+0

In questa fase non sono sicuro del perché? Puoi umorarmi? Ho tre tipi, sp, funzioni e viste. questi hanno solo parametri di input (sp e views), le funzioni hanno entrambi ... – MoonKnight

3

Non sono né Appoggiare una né sminuire questa tecnica, ma è possibile utilizzare:

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

Penso che avrei preferito separata metodi di fabbrica.

1

Si potrebbe utilizzare Lista < DbParameter>

Che è un po 'più evidente.

1

Si potrebbe provare questo:

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

È possibile utilizzare Activator.CreateInstance() come altri citati o passare un delegato Func<string, string, int, T> evitare il sovraccarico riflessione.

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

È possibile creare parametri generici di database (e le connessioni e comandi, ecc) con ADO.NET DbProviderFactories.

Lo spazio dei nomi System.Data.Common fornisce le classi per la creazione di istanze DbProviderFactory di lavorare con fonti di dati specifici. Quando si crea un'istanza DbProviderFactory e si passano le informazioni su il provider di dati, DbProviderFactory può determinare l'oggetto di connessione fortemente digitato, , restituito in base alle informazioni fornite da .

Nel codice, è possibile creare un DbProviderFactory e quindi chiamare CreateParameter().

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

io chiaramente non posso instansiate T via nuova e passare al costruttore

Come scritto, no; tuttavia, è possibile se si vincola il vostro parametro del tipo di accettare solo i tipi con i costruttori:

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

Nell'esempio di cui sopra si sarebbe in grado di dire:

T myItem = new T(); 

Nel tuo caso specifico, sembra che ti aspetti che ognuno dei tipi generici condivida qualcosa in comune. Si consideri inoltre vincolare il tipo di interfaccia:

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

che permetterebbe di, dopo aver un'istanza obiettate, applicare i valori di tutte le proprietà sull'interfaccia:

T myItem = new T(); 

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

Per ulteriori informazioni, check out Constraints on Type Parameters (C# Programming Guide) su MSDN per maggiori informazioni sui vincoli di tipo generico.

+0

Mi piace molto! Ma il fatto che gli oggetti all'interno delle mie tre strutture non siano diversi significa che non posso usare un'interfaccia perché è tutto o niente? – MoonKnight

+0

Idealmente, in questo caso, ciascuna delle strutture (o classi) sarebbe in grado di implementare (o ereditare) la stessa interfaccia o classe di base, se non altro, solo qualcosa con un metodo di fabbrica. Saresti in grado di aggiornare la tua domanda per includere le strutture reali? –

+0

Questo è stato fatto ... – MoonKnight