[Note: Cette question avait le titre original " Union de style C (ish) en C # " mais comme le commentaire de Jeff m'a informé, apparemment cette structure s'appelle une "union discriminée"]
Excusez la verbosité de cette question.
Il y a quelques questions similaires aux miennes déjà dans SO, mais elles semblent se concentrer sur les avantages d'économie de mémoire du syndicat ou de l'utiliser pour l'interopérabilité. Voici un exemple d'une telle question .
Mon désir d'avoir un truc de type syndical est quelque peu différent.
J'écris actuellement du code qui génère des objets qui ressemblent un peu à ça
public class ValueWrapper
{
public DateTime ValueCreationDate;
// ... other meta data about the value
public object ValueA;
public object ValueB;
}
Des trucs assez compliqués, je pense que vous serez d'accord. Le fait est que ValueA
cela ne peut être que de certains types (disons string
, int
et Foo
(qui est une classe) et ValueB
peut être un autre petit ensemble de types. Je n'aime pas traiter ces valeurs comme des objets (je veux la sensation chaleureuse de codage avec un peu de sécurité de type).
J'ai donc pensé à écrire une petite classe wrapper triviale pour exprimer le fait que ValueA est logiquement une référence à un type particulier. J'ai appelé la classe Union
parce que ce que j'essaie d'accomplir m'a rappelé le concept d'union en C.
public class Union<A, B, C>
{
private readonly Type type;
public readonly A a;
public readonly B b;
public readonly C c;
public A A{get {return a;}}
public B B{get {return b;}}
public C C{get {return c;}}
public Union(A a)
{
type = typeof(A);
this.a = a;
}
public Union(B b)
{
type = typeof(B);
this.b = b;
}
public Union(C c)
{
type = typeof(C);
this.c = c;
}
/// <summary>
/// Returns true if the union contains a value of type T
/// </summary>
/// <remarks>The type of T must exactly match the type</remarks>
public bool Is<T>()
{
return typeof(T) == type;
}
/// <summary>
/// Returns the union value cast to the given type.
/// </summary>
/// <remarks>If the type of T does not exactly match either X or Y, then the value <c>default(T)</c> is returned.</remarks>
public T As<T>()
{
if(Is<A>())
{
return (T)(object)a; // Is this boxing and unboxing unavoidable if I want the union to hold value types and reference types?
//return (T)x; // This will not compile: Error = "Cannot cast expression of type 'X' to 'T'."
}
if(Is<B>())
{
return (T)(object)b;
}
if(Is<C>())
{
return (T)(object)c;
}
return default(T);
}
}
Utiliser cette classe ValueWrapper ressemble maintenant à ceci
public class ValueWrapper2
{
public DateTime ValueCreationDate;
public Union<int, string, Foo> ValueA;
public Union<double, Bar, Foo> ValueB;
}
qui est quelque chose comme ce que je voulais réaliser mais il me manque un élément assez crucial - c'est la vérification de type appliquée par le compilateur lors de l'appel des fonctions Is et As, comme le montre le code suivant
public void DoSomething()
{
if(ValueA.Is<string>())
{
var s = ValueA.As<string>();
// .... do somethng
}
if(ValueA.Is<char>()) // I would really like this to be a compile error
{
char c = ValueA.As<char>();
}
}
IMO Il n'est pas valide de demander à ValueA si c'est un char
car sa définition dit clairement que ce n'est pas le cas - c'est une erreur de programmation et j'aimerais que le compilateur s'en aperçoive. [Aussi, si je pouvais obtenir cela correctement, alors (j'espère) j'obtiendrais aussi de l'intellisense - ce qui serait une aubaine.]
Pour ce faire, je voudrais dire au compilateur que le type T
peut être l'un de A, B ou C
public bool Is<T>() where T : A
or T : B // Yes I know this is not legal!
or T : C
{
return typeof(T) == type;
}
Quelqu'un a-t-il une idée si ce que je veux réaliser est possible? Ou suis-je tout simplement stupide pour avoir écrit ce cours en premier lieu?
Merci d'avance.
StructLayout(LayoutKind.Explicit)
etFieldOffset
. Cela ne peut pas être fait avec les types de référence, bien sûr. Ce que vous faites n'est pas du tout un syndicat C.