2009-11-23 4 views
5

Espero que esta pregunta no se considere demasiado subjetiva - Realmente no espero una respuesta definitiva, pero espero que la opinión de todos lo haga en menos ayúdame a formar el mío.Sugerencias necesarias: alternativa a la sobrecarga de "es" y "como" operadores en .NET

Estoy implementando un sistema de tipo personalizado, que es un superconjunto del sistema de tipo OOP clásico. En este tipo de sistema, las instancias de objeto se pueden combinar en tiempo de ejecución para formar nuevas instancias, al tiempo que se conservan las identidades individuales.

este código:

var p = new Person(); 
var pa = new Partner(p); 

... resultados en un único objeto combinado, con "p" y "pa" ser diferentes vistas OOP conformes en él. IOW, cambiar el valor de una propiedad en una de las vistas se refleja inmediatamente en cualquier otra vista que también contenga esta propiedad.

Todo esto funciona bien y bien, pero le faltan dos API clave para consultar las identidades de tipo. Realmente me gustaría ser capaz de escribir dicho código:

if (p is Partner) 
{ 
    (p as Partner).SomePartnerProperty = "..."; 
} 

Por supuesto, esto no funciona, ya que el comportamiento de "es" y "como" operadores no se pueden sobrecargar/extendió más allá de lo que es .NET Las reglas de OOP dictan. Sin embargo, todavía necesito esta característica en mi sistema de tipos.

Lo primero que pensé fue utilizar métodos de extensión genéricos que uniría a todas las instancias de mi sistema Tipo:

public static bool Is<T>(this BaseType target) where T : BaseType { ... } 
public static T As<T>(this BaseType target) where T : BaseType { ... } 

Haciendo caso omiso de la cuestión del conflicto de nombres en idiomas mayúsculas y minúsculas, esto parece estar bien en términos de funcionalidad :

if (p.Is<Partner>()) 
{ 
    p.As<Partner>().SomePartnerProperty = "..."; 
} 

sin embargo, no puede dejar de preguntarse - ¿es realmente el mejor, API más conveniente que uno puede llegar a?

¿Cómo recomendaría implementar estos dos operadores para que se sientan naturales de usar en el código de la aplicación?

ACTUALIZACIÓN: Para cualquier persona que se pregunta acerca de la finalidad de dicho sistema de tipo ... Básicamente, cada tipo cae en una de dos categorías: Identidad o papel. En el ejemplo que di más arriba, Person is an Identity (por diseño), mientras que Partner es un rol (nuevamente, por diseño, podría haber sido diseñado de manera diferente). La regla fundamental de este tipo de sistema es que cualquier número de Roles se puede componer con cualquier Identidad dada, mientras que la Identidad misma solo se puede componer con una Identidad superior (por ejemplo, una Persona puede convertirse en un Contacto, pero nunca puede convertirse en una Compañía). Tal tipo de sistema permite que las aplicaciones traten de forma transparente, por ejemplo, Objetos asociados, independientemente de la identidad que tengan (por ejemplo, persona, empresa, banco, etc.).

+1

Puñalada salvaje aquí, pero me pregunto si podría hacer esto en IronPython más fácil que en C#? Todavía podría escribir un montón de código en C#, simplemente usando IronPython cuando sea necesario. Tal vez alguien conocedor de OO Python pueda sonar. –

+0

¿Podría describirse esto como herencia por instancia (en lugar de por tipo)? Además, ¿qué sucede con el objeto 'p' subyacente cuando aplica valores específicos de' pa'? – Blixt

+0

No debe ignorar los idiomas que no distinguen entre mayúsculas y minúsculas si desea que su biblioteca tenga éxito, pero la buena noticia es que los idiomas insensibles a las mayúsculas y minúsculas (¡al menos VB!) * Pueden * funcionar con dicho código muy bien, aunque 'Is' y 'As' * son * palabras reservadas aquí. El contexto (es decir, como métodos) hace que este uso sea factible. –

Respuesta

1

Sugiero usar dos funciones simples.

El primero sería (al menos para mí) ser intuitivo con un nombre como:

p.IsType(typeHere) 
p.IsPartner() 

y el segundo un simple to-Call:

p.ToPartner() 

que no aplicaría los como genéricos (simplemente no me parece correcto), especialmente el último no.

+1

Bobby, ¿qué opina del método OfType () en Linq? Los genéricos me parecen una buena opción ... –

+1

Creo que está buscando implementar un sistema de tipos real, no clases particulares concretas como 'Partner' y' Person'. No creo que este enfoque funcione en ese escenario. –

+0

@Rob: Eso es algo que espero que sea genérico (o al menos con un argumento (del tipo)). ;) Sin embargo, nunca he trabajado con Linq. – Bobby

-1

Para mí se parece a un Partner no es un Person, sino una Partner hace tienen unaPerson. Para mí la interfaz tendría sentido como

var p = new Person(); 
var pa = new Partner(p); 

pa.SomePartnerProperty = "..."; 
pa.Person.SomePersonProperty = "..."; 
+0

esperaba esa respuesta. :) Esta es la solución OOP clásica para mi problema de diseño. Pero es menos que ideal, y puedo explicar por qué. Cuando el Socio se convierte en una propiedad de Persona ("tiene un"), esta es una relación de una sola vía, que no funciona bien con los términos de un dominio en particular, donde se considera que cada Socio es una Persona o una Compañía. Es imposible tener un acuerdo de aplicación con los Socios y aún así poder acceder a los datos de la Persona que se muestra a continuación sin algún tipo de referencia. Un sistema de tipo personalizado soluciona esto sin que "todo tenga que ser una propiedad de todo". – aoven

+0

Interesante ... Veo lo que estás tratando de hacer. No puedo pensar en una solución que me guste que no mantenga una "referencia retrospectiva" – Bob

0

Para mantener un control completo sobre el proceso de conversión (y asegurando que el código se ejecuta realmente), el enfoque parece ser la más sensata. Puede usar los operadores explicit y implicit para controlar la conversión de tipo (ya sea mediante conversiones implícitas o mediante conversión explícita), pero hay suficientes "trampas" en esos sistemas para hacer que sean una opción menos que deseable para lo que parece que es intentando arquitecto.

0

Para C# Creo que su 'primer pensamiento' (por ejemplo, x.Is<T>()) se ve bien (de improviso, creo que es lo que haría).

Si va por la ruta F #, puede usar patrones activos, p.

match p with 
| IsPartner pa -> DoPartnerStuff(pa) 
| _ -> JustPersonStuff(p) 
+1

Mientras que en el caso de solo dos alternativas, la coincidencia de patrón F # es simplemente una fantasía if/else, si el modelo completo incluye múltiples posibles papeles mutuamente exclusivos, este enfoque le da mucho más estilo de "cambio". –

+0

Agradable. Usar un lenguaje que ofrezca una mayor flexibilidad de sintaxis resuelve el problema. :) Desafortunadamente, ya sé que la mayoría de los consumidores de esta API serán miembros de la comunidad C# y VB. – aoven

0

sólo algunas de intercambio de ideas en línea ...

class Role { 
    public Role(Person p) { p.liRoles.Add(this); } 
} 
class Partner : Role { 
    public Partner(Person p) : base(p) {} 
} 
class Person { 
    List<IRole> liRoles; 
    public T As<T>() { 
     foreach(IRole r in liRoles){ if r is T return r; } 
     return null; 
    } 
    public bool Is<T>() { return As<T>() != null; } 
} 
var p = new Person(); 
var pa = new Partner(p); 

Esto debería permitir algo así como

if (p is Partner) 
    (p as Partner).PartnerMethod(); 

no he ejecutar esto a través de un compilador, sin embargo, por lo que podría estar completamente equivocado ;)

+0

Gracias, pero te perdiste el punto. El problema no es la _implementación_ de IS y AS, lo tengo todo bien cubierto. Lo que estoy buscando es la API _signature_ para IS y AS. IOW, cómo uno los usa en el código de la aplicación. Y en este sentido, su sugerencia produce el mismo efecto que mi original. También conserva el mismo problema: es decir, el ligero abuso de los métodos genéricos (el argumento T en Is no se usa realmente en la firma), que es, naturalmente, recogido por FxCop. – aoven

0

Espera ... No veo lo que está mal con el código OO normal (a menos que tengas toneladas de clase)

interface IPartner {...} 
interface IPerson {...} 
interface ICompany {...} 

class Person : IPerson {...} 
class PersonPartner : Person, IPartner {...} 
class Company : ICompany {...} 
class CompanyPartner : Company, IPartner {...} 

O supuesto, esto sería más fácil con la herencia múltiple ...

+0

Las jerarquías reales en mi sistema de tipo dan como resultado un gran número de combinaciones posibles. Tener que declarar una clase para cada combinación no es el resultado deseado. Si lo fuera, no habría necesidad de un sistema de tipo personalizado.:) – aoven

1

Mi lectura de lo que la pregunta es después sería un sistema mixin para C# - como se discutió en Is it possible to implement mixins in C#?. La conclusión es que hay algunas maneras de obtener la mayor parte de lo que una sintaxis de mixin adecuada permitiría, aunque personalmente nunca he encontrado estos resultados para dar un buen resultado de costo/beneficio.

+0

¡Y bien estarías! Es básicamente mixins, con algunas restricciones especiales sobre las composiciones válidas salpicadas en la parte superior. Gracias por el enlace. De alguna manera me perdí ese hilo. – aoven

0

He estado pensando mucho últimamente sobre estos conceptos, y sobre traits in C#. Creo que la idea que tienes para los métodos de extensión de prueba de tipo no es mucho mejor que eso (es casi exactamente lo que se me ocurrió). Tomando prestada alguna terminología del Perl 6 roles, llamaría a su método IsDoes. Pero dejaría sin cambios el As.

Pero, no creo que este uso se siente natural para lo que estamos tratando de lograr:

var p = new Person(); 
var pa = new Partner(p); 

No se ve como que va apuntan a la misma instancia. Ahora tomando prestado de Scala, tal vez algo así en su lugar?

var p = new Person(); 
var pa = p.With<Partner>(); 

De todos modos, ¿terminaste tu API? ¿Es de código abierto?

Cuestiones relacionadas