Aquí hay un ejemplo práctico sobre cómo le gustaría tener un parámetro de tipo de constructor adicional y la solución alternativa.
voy a introducir un simple envoltorio para RefCounted
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;
}
Esto parece estar bien. Pero tarde o temprano le gustaría lanzar un RefCounted<Control>
a RefCounted<Button>
mientras mantiene el recuento de referencias de objeto, es decir, solo cuando ambas instancias están dispuestas a deshacerse del objeto subyacente.
La mejor manera es si se puede escribir (como las personas C++ pueden hacer)
public RefCounted(RefCounted<U> other)
{
...whatever...
}
Pero C# no permite esto. Entonces la solución es usar algo indirecto.
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 su clase tiene campos que son de tipo genérico, no tiene más remedio que poner todos esos tipos a la clase. Sin embargo, si solo quieres ocultar algún tipo del constructor, necesitarás usar el truco anterior: tener un constructor oculto para juntar todo y definir una función genérica normal para llamar a ese constructor.
Podría evitar el problema de clase del mismo nombre al no permitir una clase genérica y no genérica con el mismo nombre (en serio, ¿C# permite esto?). Para el constructor genérico de la clase genérica, no creo que sea demasiado horrible, solo es genérico de mayor orden. –
Una observación: el constructor de la clase genérica es genérico porque la clase es genérica. Sin embargo (tomando su respuesta) no puede especificar ** argumentos ** genéricos adicionales. ¡Gracias por muy buenos ejemplos! – greenoldman
@Peter: No, el problema de clase con nombre samed no es un problema, porque aunque * puede * "sobrecargar" clases por tipo de letra, no hay ambigüedad. Ver 'Tuple' para un ejemplo de eso. –