estoy bastante seguro de que está fuera de suerte por lo que usando el compilador y genéricos para ahorrar algunas comprobaciones en tiempo de ejecución. No puede anular algo que aún no existe, y no puede tener diferentes tipos de devolución para los mismos métodos.
No puedo decir que entiendo completamente su motivación, pero tiene un mérito técnico.
Mi primer intento fue utilizar la clase base con una interfaz pública no virtual y luego tener otro método virtual protegido CheckCreatedType
que permitiera que cualquier elemento de la cadena inspeccionara el tipo antes de llamar a la clase base Crear.
public class A
{
public IFieldSimpleItem Create()
{
IFieldSimpleItem created = InternalCreate();
CheckCreatedType(created);
return created;
}
protected virtual IFieldSimpleItem InternalCreate()
{
return new SimpleImpl();
}
protected virtual void CheckCreatedType(IFieldSimpleItem item)
{
// base class doesn't care. compiler guarantees IFieldSimpleItem
}
}
public class B : A
{
protected override IFieldSimpleItem InternalCreate()
{
// does not call base class.
return new NormalImpl();
}
protected override void CheckCreatedType(IFieldSimpleItem item)
{
base.CheckCreatedType(item);
if (!(item is IFieldNormalItem))
throw new Exception("I need a normal item.");
}
}
Los siguientes controles en el tiempo de ejecución se comprueban en la clase base.El problema irresoluble es que todavía tiene que confiar en el método de clase base que se llama. Una subclase que se comporta mal puede romper todos los controles al no llamar al base.CheckCreatedType(item)
.
Las alternativas son codificar todas las verificaciones de todas las subclases dentro de la clase base (malas) o externalizar la verificación.
Intento 2: (Sub) Las clases registran los cheques que necesitan.
public class A
{
public IFieldSimpleItem Create()
{
IFieldSimpleItem created = InternalCreate();
CheckCreatedType(created);
return created;
}
protected virtual IFieldSimpleItem InternalCreate()
{
return new SimpleImpl();
}
private void CheckCreatedType(IFieldSimpleItem item)
{
Type inspect = this.GetType();
bool keepgoing = true;
while (keepgoing)
{
string name = inspect.FullName;
if (CheckDelegateMethods.ContainsKey(name))
{
var checkDelegate = CheckDelegateMethods[name];
if (!checkDelegate(item))
throw new Exception("failed check");
}
if (inspect == typeof(A))
{
keepgoing = false;
}
else
{
inspect = inspect.BaseType;
}
}
}
private static Dictionary<string,Func<IFieldSimpleItem,bool>> CheckDelegateMethods = new Dictionary<string,Func<IFieldSimpleItem,bool>>();
protected static void RegisterCheckOnType(string name, Func<IFieldSimpleItem,bool> checkMethod)
{
CheckDelegateMethods.Add(name, checkMethod);
}
}
public class B : A
{
static B()
{
RegisterCheckOnType(typeof(B).FullName, o => o is IFieldNormalItem);
}
protected override IFieldSimpleItem InternalCreate()
{
// does not call base class.
return new NormalImpl();
}
}
La comprobación se realiza por la subclase registrar un delegado para invocar en la clase base, pero sin la clase base conocer todas las reglas por adelantado. Tenga en cuenta también que sigue siendo la interfaz pública no virtual que permite a la clase base comprobar los resultados antes de devolverlos.
Supongo que es un error del desarrollador que está tratando de atrapar. Si es aplicable, puede adornar el método de comprobación de tiempo de ejecución con System.Diagnostics.Conditional("DEBUG")]
, lo que permite que la versión Release omita las comprobaciones.
Mi conocimiento de los genéricos no es perfecto, así que quizás esto sea innecesario. Sin embargo, los controles aquí no tienen que ser solo para tipo: esto podría adaptarse para otros usos. p.ej. el delegado pasado en Register..
no tiene que simplemente verificar que la referencia es un tipo específico '
* Tenga en cuenta que probablemente no sea bueno crear el diccionario en el nombre de tipo como se escribió anteriormente; este trabajo es un poco simplista para ilustrar el mecanismo utilizado.
Gracias, aunque esta comprobación se realiza en tiempo de ejecución, creo que esto es lo más cercano que voy a conseguir. Cheers – vdhant