Controllo del tipo generico
-
08-06-2019 - |
Domanda
Esiste un modo per imporre/limitare i tipi passati alle primitive? (bool, int, stringa, ecc.)
Ora, so che puoi limitare il parametro di tipo generico a un'implementazione di tipo o interfaccia tramite il file Dove clausola.Tuttavia, questo non si adatta al conto dei primitivi (AFAIK) perché non hanno tutti un terreno comune (a parte oggetto prima che qualcuno lo dica!:P).
Quindi, il mio pensiero attuale è semplicemente stringere i denti e fare un grande interruttore dichiarazione e lanciare un ArgumentException in caso di fallimento..
MODIFICA 1:
Giusto per chiarire:
La definizione del codice dovrebbe essere come:
public class MyClass<GenericType> ....
E istanziazione:
MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)
MODIFICA 2
@Jon Limjap - Giusta osservazione, ed è qualcosa che stavo già considerando...Sono sicuro che esista un metodo generico che può essere utilizzato per determinare se il tipo è di valore o di riferimento.
Questo potrebbe essere utile per rimuovere istantaneamente molti oggetti di cui non voglio occuparmi (ma poi devi preoccuparti delle strutture utilizzate come Misurare )..Problema interessante, no?:)
Ecco qui:
where T : struct
Preso da MSDN.
Sono curioso..È possibile eseguire questa operazione in .NET 3.x utilizzando metodi di estensione?Creare un'interfaccia e implementarla nei metodi di estensione (che probabilmente sarebbe più pulito di un cambio un po' grosso).Inoltre, se in seguito è necessario estenderlo a qualsiasi tipo personalizzato leggero, è anche possibile implementare la stessa interfaccia, senza che siano necessarie modifiche al codice di base.
Che cosa ne pensate?
La triste notizia è che sto lavorando nel Framework 2!!:D
MODIFICA 3
Questo è stato così semplice da seguire Puntatore di Jon Limjaps..Così semplice che mi viene quasi da piangere, ma è fantastico perché il codice funziona a meraviglia!
Quindi ecco cosa ho fatto (riderai!):
Codice aggiunto alla classe generica
bool TypeValid()
{
// Get the TypeCode from the Primitive Type
TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));
// All of the TypeCode Enumeration refer Primitive Types
// with the exception of Object and Empty (Null).
// Since I am willing to allow Null Types (at this time)
// all we need to check for is Object!
switch (code)
{
case TypeCode.Object:
return false;
default:
return true;
}
}
Quindi un piccolo metodo di utilità per verificare il tipo e lanciare un'eccezione,
private void EnforcePrimitiveType()
{
if (!TypeValid())
throw new InvalidOperationException(
"Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name +
"' - this Class is Designed to Work with Primitive Data Types Only.");
}
Allora non resta che chiamare ApplicaPrimitiveType() nei costruttori delle classi.Lavoro fatto!:-)
L'unico aspetto negativo è che genera un'eccezione solo in fase di esecuzione (ovviamente) anziché in fase di progettazione.Ma questo non è un grosso problema e potrebbe essere risolto con utilità come FxCop (che non usiamo al lavoro).
Un ringraziamento speciale a Jon Limjap per questo!
Soluzione
Le primitive sembrano essere specificate in TypeCode
enumerazione:
Forse c'è un modo per scoprire se un oggetto contiene il file TypeCode enum
senza doverlo lanciare su un oggetto o una chiamata specifici GetType()
O typeof()
?
Aggiornamento Era proprio sotto il mio naso.L'esempio di codice mostra questo:
static void WriteObjectInfo(object testObject)
{
TypeCode typeCode = Type.GetTypeCode( testObject.GetType() );
switch( typeCode )
{
case TypeCode.Boolean:
Console.WriteLine("Boolean: {0}", testObject);
break;
case TypeCode.Double:
Console.WriteLine("Double: {0}", testObject);
break;
default:
Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
break;
}
}
}
È ancora un brutto cambiamento.Ma è un buon punto di partenza!
Altri suggerimenti
public class Class1<GenericType> where GenericType : struct
{
}
Questo sembrava fare il suo lavoro..
Più o meno quello che ha già detto @Lars:
//Force T to be a value (primitive) type.
public class Class1<T> where T: struct
//Force T to be a reference type.
public class Class1<T> where T: class
//Force T to be a parameterless constructor.
public class Class1<T> where T: new()
Funzionano tutti in .NET 2, 3 e 3.5.
Se riesci a tollerare l'utilizzo dei metodi factory (invece dei costruttori MyClass che hai richiesto) potresti sempre fare qualcosa del genere:
class MyClass<T>
{
private readonly T _value;
private MyClass(T value) { _value = value; }
public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
// etc for all the primitive types, or whatever other fixed set of types you are concerned about
}
Un problema qui è che dovresti digitare MyClass<AnyTypeItDoesntMatter>.FromInt32
, il che è fastidioso.Non esiste un ottimo modo per aggirare questo problema se si desidera mantenere la riservatezza del costruttore, ma ecco un paio di soluzioni alternative:
- Crea una classe astratta
MyClass
.FareMyClass<T>
ereditare daMyClass
e annidarlo all'internoMyClass
.Sposta i metodi statici inMyClass
.In questo modo tutta la visibilità funzionerà, a costo di dover accedereMyClass<T>
COMEMyClass.MyClass<T>
. - Utilizzo
MyClass<T>
come dato.Crea una classe staticaMyClass
che richiama i metodi staticiMyClass<T>
utilizzandoMyClass<AnyTypeItDoesntMatter>
(probabilmente usando ogni volta il tipo appropriato, solo per ridere). - (Più facile, ma certamente strano) Crea un tipo astratto
MyClass
da cui ereditaMyClass<AnyTypeItDoesntMatter>
.(Per concretezza, diciamoMyClass<int>
.) Poiché è possibile chiamare metodi statici definiti in una classe base tramite il nome di una classe derivata, ora è possibile utilizzareMyClass.FromString
.
Questo ti dà il controllo statico a scapito di più scrittura.
Se sei soddisfatto del controllo dinamico, utilizzerei qualche variazione sulla soluzione TypeCode sopra.
@Rapinare, Enum
scivolerà attraverso il TypeValid
funzionare così com'è TypeCode
È Integer
.Ho aggiornato anche la funzione per controllare Enum
.
Private Function TypeValid() As Boolean
Dim g As Type = GetType(T)
Dim code As TypeCode = Type.GetTypeCode(g)
' All of the TypeCode Enumeration refer Primitive Types
' with the exception of Object and Empty (Nothing).
' Note: must also catch Enum as its type is Integer.
Select Case code
Case TypeCode.Object
Return False
Case Else
' Enum's TypeCode is Integer, so check BaseType
If g.BaseType Is GetType(System.Enum) Then
Return False
Else
Return True
End If
End Select
End Function
Puoi semplificare il EnforcePrimitiveType
metodo utilizzando typeof(PrimitiveDataType).IsPrimitive
proprietà.Mi sto perdendo qualcosa?
Usa un'abitudine FxCop regola che segnala l'uso indesiderato di MyClass<>
.
Avendo una sfida simile, mi chiedevo come vi sentiste riguardo al Interfaccia IConvertibile.Consente ciò che richiede il richiedente e puoi estenderlo con le tue implementazioni.
Esempio:
public class MyClass<TKey>
where TKey : IConvertible
{
// class intentionally abbreviated
}
Sto pensando a questa come a una soluzione, anche se molti dei suggerimenti facevano parte della mia selezione.
La mia preoccupazione è, tuttavia, che sia fuorviante per i potenziali sviluppatori che utilizzano la tua classe?
Saluti e grazie.