2009-10-02 25 views
7

Estoy en la situación en que muchas de mis clases son contenedores de objetos conocidos pero no ordenados de diferentes tipos, p. un recipiente puede ser similar al siguiente:C# más cercano equivalente a la expresión de coincidencia F #?

public class Container 
{ 
    public A A { get; private set; } 
    public B B { get; private set; } 
    public C C { get; private set; } 

    public bool StoreIfKnown(object o) 
    { 
     // TODO... 
    } 
} 

Así que si o es de tipo A Debe ser almacenado en el A propiedad, tipo B en la propiedad B y así sucesivamente.

En F # el método StoreIfKnown se podría escribir algo como lo siguiente (con perdón de los errores de sintaxis, mi F # no es muy grande y bastante oxidado):

match o with 
| ?: A a -> A <- a; true 
| ?: B b -> B <- b; true 
| ?: C c -> C <- c; true 
| _ -> false 

Pero en C# la única manera parece ser el lugar verbosa:

if (o is A) 
{ 
    this.A = (A)o; 
    return true; 
} 

if (o is B) 
{ 
    this.B = (B)o; 
    return true; 
} 

// etc. 

return false; 

que podría hacerlo con la palabra clave as para evitar el patrón de prueba/cast que sería más rápido, pero es aún más detallado.

¿Hay alguna manera elegante de hacer esto en C#?

+0

estoy impactado en realidad hay alguien por ahí usando F # ... – pixelbobby

+3

@pixelbobby: observo que hay 478 F # preguntas, en comparación con 409 Haskell, Scala 382, ​​353 Lisp, 305 Erlang, y 96 OCaml. F # parece ser el lenguaje de programación funcional más popular en SO, probablemente debido al cruce de la comunidad hubFS y los desarrolladores .NET que quieren saltar al tren de programación funcional. – Juliet

+5

No diría que uso F #, solo me gusta estar al tanto de sus características. ¡Aunque estaría ansioso por tener un trabajo donde fuera el idioma principal que utilicé (asumiendo la idoneidad para el propósito) para mejorar mis habilidades! Conocer al menos los conceptos básicos de varios idiomas que utilizan paradigmas diferentes a los de los idiomas en los que normalmente trabaja, mejora su capacidad de programación integral. –

Respuesta

11

Se podía autor un método de extensión en 'o' y clases de ayuda para permitir un modelo de programación como

o.Match<A>(a => { this.A = a; return true; }) 
.Match<B>(b => { this.B = b; return true; }) 
.Else(() => { return false; }) 

Pero tener cuidado de hacer demasiado hackery DSL-como aquí, para que no se termina con una API solo tú entiendes

Ver también

http://blogs.msdn.com/lucabol/archive/2008/07/15/a-c-library-to-write-functional-code-part-v-the-match-operator.aspx

+0

enfoque muy elegante ... –

+2

Y ponga el método de extensión en su propio espacio de nombres, o la gente se quejará de que ha agregado dos métodos a la lista intellisense para todos los tipos. –

+0

Sí, me preguntaba acerca de algo como esto. Tal como se publicó, la inferencia tipo no funciona del todo bien (debería escribir Match , que es un poco desordenado), pero después de jugar un poco creo que puedo hacer algo similar y eliminar ese problema. Publicaré los resultados cuando haya terminado. –

9

No es tan ingenioso como solución de Brian, pero esto no requiere la definición de un nuevo DSL. Se dará cuenta de que su repitiendo el siguiente código:

if (o is {DataType}) 
{ 
    {Property} = ({DataType})o; 
    return true; 
} 

Su fácil suficiente para tirar de esa plantilla en su propio método, lo que resulta en algo como esto:

public class Container 
{ 
    public A A { get; private set; } 
    public B B { get; private set; } 
    public C C { get; private set; } 

    private bool TestProp<T>(object o, Action<T> f) 
    { 
     if (o is T) 
      return false; 

     f((T)o); 
     return true; 
    } 

    public bool StoreIfKnown(object o) 
    { 
     return 
      TestProp<A>(o, x => A = x) || 
      TestProp<B>(o, x => B = x) || 
      TestProp<C>(o, x => C = x) || 
      false; 
    } 
} 

Si se trabaja con tipos de referencia , usted puede tomar ventaja de la inferencia de tipos con los siguientes ajustes:

private bool TestProp<T>(T o, Action<T> f) 
    { 
     if (o == null) 
      return false; 

     f(o); 
     return true; 
    } 

    public bool StoreIfKnown(object o) 
    { 
     return 
      TestProp(o as A, x => A = x) || 
      TestProp(o as B, x => B = x) || 
      TestProp(o as C, x => C = x) || 
      false; 
    } 
+0

+1 - Esto es en realidad bastante similar a lo que tengo en este momento (aunque no quería nublar la pregunta con tantos detalles). Funciona bastante bien, pero se siente un poco torpe. –

+2

Mientras que el enfoque de Brians es más inteligente, esta es la forma en que lo implementaría en C#. Muy limpio y directo. El enfoque F # se ve muy bien en F #, pero no tanto en C#. –

+0

Tenga en cuenta que puede omitir los valores finales 'falsos', ya que 'a || falso' es equivalente a' a', y 'a || b || falso' es equivalente a' a || b', etc. – kvb

8

he estado jugando con un pequeño generador de juego (inspirado en Brian's answer) WHI ch permite verificación de tipos, cláusulas de guardia y devolución de un resultado de todo el asunto. Utiliza la inferencia de tipo por lo que el único lugar donde necesita especificar un tipo es donde realmente desea.

Así, imaginando tipo C tiene un IsActive propiedad que queremos ser true, se vería algo como esto:

var stored = Match.Against(o) 
    .When<A>().Then(a => { this.A = a; return true; }) 
    .When<B>().Then(b => { this.B = b; return true; }) 
    .When<C>(c => c.IsActive).Then(c => { this.C = c; return true; }) 
    .Otherwise(a => false); 

que creo que es bastante fácil de leer, especialmente ya que permite un predicado que se ejecute contra el tipo derivado antes de realmente coincidir, que es algo que realmente necesito.

El código es bastante extenso, ya que necesita varias clases de compilador parcialmente especificadas en segundo plano para permitir que la inferencia de tipo funcione, por lo que realmente no puedo publicarla aquí.Pero si alguien está interesado, házmelo saber en los comentarios y lo incluiré en mi blog y pondré un enlace aquí.

+0

+1: Awesomeness :) – Juliet

2

Bart de Smet una vez fue loco con coincidencia de patrón, comenzando here (sube hasta la parte 8). Si alguna vez logras atravesar todo este contenido, no debería quedar ninguna pregunta para la coincidencia de patrones en C#. Si los hay, probablemente no puedan ser respondidos por stackoverflow :)

+0

Parece interesante, voy a leerlo. Aunque desafortunadamente parece que su sitio acaba de romperse temporalmente: -S –

1

A partir de agosto de 2016 y la vista previa de C# 7.0, there is a limited support for pattern matching. Puede intentarlo si usa Visual Studio “15” Preview 4.

Según el blog de MSDN, puede utilizar patrones en dos lugares:

  • en el lado derecho de la es expresiones

  • en los cláusulas de casos en interruptor declaraciones

patrones posibles son:

  • patrones constantes de la forma c (donde c es una expresión constante en C#), que prueba que la entrada es igual a C

  • patrones de tipo de la forma T x (donde T es un tipo yx es un identificador), que prueba que la entrada tiene tipo T, y si es así, extrae el valor de la entrada en una nueva variable x del tipo T

  • Var patrones de la forma var x (donde x es un identificador), que siempre coincide, y simplemente pone el valor de la inp UT en una variable x fresco con el mismo tipo que el de entrada

que no se ha instalado Visual Studio 15, así que no estoy seguro de que me volvió a escribir el código correctamente, pero no debe estar muy lejos :

public class Container 
{ 
    public A A { get; private set; } 
    public B B { get; private set; } 
    public C C { get; private set; } 

    public bool StoreIfKnown(object obj) 
    { 
     switch (obj) 
     { 
      case A a: 
       this.A = a 
       // I don't put "break" because I'm returning value from a method 
       return true; 
      case B b: 
       this.B = b 
       return true; 
      case C c: 
       this.C = c 
       return true; 
      default: 
       WriteLine("<other>"); 
       return false; 
     } 
    } 
} 
Cuestiones relacionadas