Voici un exemple concret de la manière dont vous souhaitez disposer d'un paramètre supplémentaire pour le type de constructeur, ainsi que la solution de contournement.
Je vais introduire une simple RefCounted
pour IDisposable
:
public class RefCounted<T> where T : IDisposable
{
public RefCounted(T value)
{
innerValue = value;
refCount = 1;
}
public void AddRef()
{
Interlocked.Increment(ref refCount);
}
public void Dispose()
{
if(InterlockedDecrement(ref refCount)<=0)
innerValue.Dispose();
}
private int refCount;
private readonly innerValue;
}
Cela semble bien se passer. Mais tôt ou tard, vous souhaitez lancer un RefCounted<Control>
a RefCounted<Button>
tout en conservant le comptage des références des deux objets, c'est-à-dire qu'il suffit de disposer des deux instances pour disposer de l'objet sous-jacent.
Le meilleur moyen est d'écrire (comme les gens du C++ peuvent le faire)
public RefCounted(RefCounted<U> other)
{
...whatever...
}
Mais le C# ne le permet pas. La solution est donc d'utiliser une certaine indirection.
private readonly Func<T> valueProvider;
private readonly Action disposer;
private RefCounted(Func<T> value_provider, Action disposer)
{
this.valueProvider = value_provider;
this.disposer = disposer;
}
public RefCounted(T value) : this(() => value, value.Dispose)
{
}
public RefCounted<U> Cast<U>() where U : T
{
AddRef();
return new RefCounted<U>(() => (U)(valueProvider()),this.Dispose);
}
public void Dispose(){
if(InterlockedDecrement(ref refCount)<=0)
disposer();
}
Si votre classe a des champs de type générique, vous n'avez pas d'autre choix que de mettre tous ces types dans la classe. Cependant, si vous voulez simplement cacher certains types du constructeur, vous devrez utiliser l'astuce ci-dessus - avoir un constructeur caché pour tout mettre ensemble, et définir une fonction générique normale pour appeler ce constructeur.