2012-01-26 10 views
7

que tienen una docena de métodos en mi proyecto (C# 2.0) que se parecen a esto:genéricos sin estrenar()

internal bool ValidateHotelStayEntity(DataRow row) 
{ 
    return (new HotelStayEntity(row)).Validate(); 
} 

... pero por diferentes clases de 'entidad'. (Bueno, no es tan trivial, he simplificado el código aquí.)

Parece un buen candidato para los genéricos y se me ocurrió esto:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase 
{ 
    return (new T(row)).Validate(); 
} 

Y, por supuesto, me sale el " No se puede crear una instancia del tipo parametrer 'T' porque no tiene el error new() constraint ".

El problema es que estas clases de "entidad" no tienen un constructor público sin parámetros, ni una forma de agregar los datos de la "fila" posteriormente. Y como EntityBase es parte del marco de trabajo de la compañía, no tengo control sobre él (es decir, no puedo cambiarlo).

¿Hay alguna forma de evitar esto?

+3

No tengo tiempo para un ejemplo ahora, pero puede crear un método de fábrica genérico que se pueda pasar por la fila. –

+1

¿Por qué estás pasando DataRows en lugar de tus Entidades? ¿No deberían las Entidades haber sido validadas mucho antes de que sean tratadas como DataRows? –

+0

No eran realmente DataRows, eran obsequios similares a DataRow que 'simplifiqué' para esta publicación. No quería desviarme para explicar el marco impuesto con el que tengo que lidiar. –

Respuesta

3

embargo, otra forma podría ser involucrar a la reflexión en el precio del tiempo de compilación y una disminución del rendimiento:

internal bool ValidateEntity<T>(DataRow row) 
{ 
    object entity = Activator.CreateInstance(typeof(T), new object[] { row }); 
    MethodInfo validate = typeof(T).GetMethod("Validate"); 
    return (bool) validate.Invoke(entity, new object[]); 
} 

Tenga en cuenta que esto funcionaría incluso si las entidades no tienen un ancestro común

+0

Si Validate() arrojará una excepción, se ajustará a TargetInvocationException, no creo que sea bueno. Casting para EntityBase parece ser mucho más preferible. –

+1

Puede almacenar en caché ese MethodInfo en una variable estática (porque es genérico por tipo cerrado). Ahora elimina la necesidad de reflejarlo para cada llamada. –

+0

@MikeBrown Sí, puedes, pero deberás mantener un diccionario de Type to MethodInfo (las clases pueden no tener un ancestro común) –

15

Una forma sencilla es proporcionar una función de fábrica:

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) 
    where T : EntityBase 
{ 
    return factory(row).Validate(); 
} 

y llame a:

bool valid = ValidateEntity(row, x => new Foo(x)); 

mente que, en ese momento es más complicado que simplemente llamando

bool valid = new Foo(row).Validate() 

en primer lugar ...

No está claro qué intenta lograr en su contexto real, pero este tipo de enfoque de fábrica/proveedor puede ser útil en otros momentos. Tenga en cuenta que llamar a un delegado de fábrica también puede ser considerablemente más rápido que usar new T() con una restricción, as I blogged a while ago. Irrevelante en muchos casos, pero vale la pena conocerlo.

EDIT: Para compatibilidad .NET 2.0 se necesitaría para declarar el delegado sí mismo, sino que es fácil:

public delegate TResult Func<T, TResult>(T input); 

Si eres realmente usando C# 2 (en lugar de, digamos, C# 3 la orientación .NET 2.0), entonces no será capaz de utilizar las expresiones lambda o bien, pero todavía se puede utilizar métodos anónimos:

bool valid = ValidateEntity(row, delegate(DataRow x) { return new Foo(x); }); 
+1

No creo que haya un Func en 2.0. –

+0

@PeterTrevor: Vaya, no había notado el requisito de .NET 2.0, pero puede definir fácilmente a sus propios delegados. Se editará para dar un ejemplo de eso. –

+0

+1 Como la fábrica sería mucho mejor que la reflexión. – Shibumi

1

Un ejemplo de solución método de fábrica @JohnSaunders':

internal bool ValidateEntity<T>(DataRow row, Func<DataRow, T> factory) where T : EntityBase 
{ 
    return (factory(row)).Validate(); 
} 
1

Puede hacer algo como esto:

internal bool ValidateEntity<T>(DataRow row) where T : EntityBase 
{ 
    return ((T)Activator.CreateInstance(typeof(T), new object[] { row })).Validate(); 
}