Se si cerca di fare una struct generico con l'attributo [ StructLayout (LayoutKind .Explicit)]
, usando l'struct genera un'eccezione in fase di esecuzione:Perché i tipi generici non possono avere un layout esplicito?
System.TypeLoadException: Impossibile caricare il tipo 'foo' da 'bar' assemblaggio perché tipi generici non può avere un layout esplicito.
Ho avuto difficoltà a trovare alcuna prova che questa limitazione esiste anche. I documenti Type.IsExplicitLayout
implicano fortemente che è consentito e supportato. Qualcuno sa perché questo non è permesso? Non riesco a pensare a nessun motivo per cui i tipi generici potrebbero renderlo meno verificabile. Mi sembra un caso limite che semplicemente non si sono preoccupati di implementare.
Ecco an example del motivo per cui il layout generico esplicita sarebbe utile:
public struct TaggedUnion<T1,T2>
{
public TaggedUnion(T1 value) { _union=new _Union{Type1=value}; _id=1; }
public TaggedUnion(T2 value) { _union=new _Union{Type2=value}; _id=2; }
public T1 Type1 { get{ if(_id!=1)_TypeError(1); return _union.Type1; } set{ _union.Type1=value; _id=1; } }
public T2 Type2 { get{ if(_id!=2)_TypeError(2); return _union.Type2; } set{ _union.Type2=value; _id=2; } }
public static explicit operator T1(TaggedUnion<T1,T2> value) { return value.Type1; }
public static explicit operator T2(TaggedUnion<T1,T2> value) { return value.Type2; }
public static implicit operator TaggedUnion<T1,T2>(T1 value) { return new TaggedUnion<T1,T2>(value); }
public static implicit operator TaggedUnion<T1,T2>(T2 value) { return new TaggedUnion<T1,T2>(value); }
public byte Tag {get{ return _id; }}
public Type GetUnionType() {switch(_id){ case 1:return typeof(T1); case 2:return typeof(T2); default:return typeof(void); }}
_Union _union;
byte _id;
void _TypeError(byte id) { throw new InvalidCastException(/* todo */); }
[StructLayout(LayoutKind.Explicit)]
struct _Union
{
[FieldOffset(0)] public T1 Type1;
[FieldOffset(0)] public T2 Type2;
}
}
utilizzo:
TaggedUnion<int, double> foo = 1;
Debug.Assert(foo.GetUnionType() == typeof(int));
foo = 1.0;
Debug.Assert(foo.GetUnionType() == typeof(double));
double bar = (double) foo;
Edit:
per essere chiari, si noti che i layout non sono verificate al momento della compilazione anche se la struttura non è generica. Le differenze di sovrapposizione e x64 di riferimento sono rilevate in fase di esecuzione dal CLR: http://pastebin.com/4RZ6dZ3S Mi chiedo perché i generici sono limitati quando i controlli vengono eseguiti in runtime in entrambi i casi.
Per la cronologia, lo stesso tipo di "restrizioni imposte" potrebbe essere implementato lanciando dal costruttore statico. (E non sarei sorpreso di vederlo fatto in pratica poiché i limiti generici sono assurdamente sottodimensionati.) Ma immagino che non sarei sorpreso se quello fosse davvero il loro fondamento logico per questa limitazione. – DBN
@DBN - La mia ipotesi è che sia davvero la logica, altrimenti si potrebbe sostenere che il sistema di restrizione dei tipi potrebbe essere sballato al posto delle eccezioni. Sono anche d'accordo con te, sono sottodimensionati. Sul lato positivo, le implementazioni C#/CTS sono molto più facili da scrivere rispetto a C++. Come scrittore di compilatori, posso realisticamente avere la possibilità di implementare la mia lingua con i generici CTS senza assistenza. Con i modelli C++, è meglio avere una squadra con te. Devo ammettere, C# è un design relativamente pulito. – codenheim
Non vedo alcun bel percorso per aggiungere ulteriori tipi di vincoli generici al sistema di tipi, che presuppone un insieme di elementi non ereditabili molto solidi [nuovo, struct e classe]. Mentre potrebbe essere utile avere un vincolo per es. "copiabile come valore" (che consente di definire strutture che * non erano *), attualmente non richiede alcun vincolo generico e quindi nessun tipo generico esistente sarebbe in grado di soddisfarlo. Quello che sarebbe probabilmente necessario sarebbe avere un anti-vincolo, tale che le strutture che non erano copiabili come valore potrebbero * solo * essere passate a parametri generici ... – supercat