2009-02-05 26 views
24

Sé que se puede hacer en Java, ya que he usado esta técnica bastante extensamente en el pasado. Un ejemplo en Java se mostraría a continuación. (Pregunta adicional. ¿Qué es esta técnica llamada? Es difícil encontrar un ejemplo de esto sin un nombre.)C#: Creando una instancia de una clase abstracta sin definir la nueva clase

public abstract class Example { 
    public abstract void doStuff(); 
} 

public class StartHere{ 
    public static void main(string[] args){ 
     Example x = new Example(){ 
     public void doStuff(){ 
      System.out.println("Did stuff"); 
     }    
     }; 
     x.doStuff(); 
    } 
} 

Ahora, mi pregunta principal sería, puede esto también se puede hacer en C#, y si es así, ¿cómo?

Respuesta

16

Con expresiones lamba y clase inicializadores puede obtener el mismo comportamiento con un poco de esfuerzo.

public class Example { 
    public Action DoStuff; 
    public Action<int> DoStuffWithParameter; 
    public Func<int> DoStuffWithReturnValue; 
} 

class Program { 
    static void Main(string[] args) { 
     var x = new Example() { 
      DoStuff =() => { 
       Console.WriteLine("Did Stuff"); 
      }, 
      DoStuffWithParameter = (p) => { 
       Console.WriteLine("Did Stuff with parameter " + p); 
      }, 
      DoStuffWithReturnValue =() => { return 99; } 


     }; 

     x.DoStuff(); 
     x.DoStuffWithParameter(10); 
     int value = x.DoStuffWithReturnValue(); 
     Console.WriteLine("Return value " + value); 
     Console.ReadLine(); 
    } 
} 

Un problema con esta solución que me he dado cuenta es que si usted fuera a crear campos de la clase Ejemplo, las expresiones lambda no sería capaz de acceder a esos campos.

Sin embargo, no hay ninguna razón por la que no podría pasar la instancia de Ejemplo a las expresiones lambda que les daría acceso a cualquier estado público que pueda contener el ejemplo. AFAIK que sería funcionalmente equivalente a la clase interna anónima de Java.

P.S. Si vas a votar una respuesta, haznos un favor a todos y agrega un comentario sobre por qué no estás de acuerdo :-)

+2

Esto es ** no * * el mismo comportamiento. La parte más útil de las clases internas anónimas es poder extender rápidamente una clase sin tener que definir un tipo nuevo completo. – makhdumi

8

Eso no se puede hacer en C#; necesita declarar un nuevo tipo de clase. Lo más cerca que se puede obtener en C# es probablemente una clase anidada llamada:

public class StartHere{ 
    private class Foo : Example { 
     public override void doStuff() 
     { 
      Console.WriteLine("did stuff"); 
     } 
    } 
    public static void Main(string[] args){ 
     Example x = new Foo(); 
     x.doStuff(); 
    } 
} 
+0

Vale la pena mencionar que un 'class' privado interno en C# coincidiría con Java de' Clase interna de clase estática privada, y por lo tanto no puede acceder a los miembros de la clase contenedora. –

+0

@ Jean-PhilippePellet eso no es verdad; una 'clase privada' en C# * no * tiene acceso a * miembros * de la clase que los contiene (incluso los miembros' privados'). Lo que le falta es una * instancia * de la clase que lo contiene. Si una instancia está disponible para la clase anidada, la clase anidada tiene acceso completo a los miembros. –

+0

OK, gracias por la aclaración; ¡Pronuncié mi oración (y usé la palabra "acceso") mal! –

0

En resumen, no, usted tiene que definir como subclase separada. Creo que esta característica viene C# 4.0 sin embargo?

Edit: No, no viene C# 4.0 Lo inventé.

+0

No tengo conocimiento de nada en C# 4.0 con esas características. –

+0

Era la palabra clave dinámica en la que estaba pensando –

+0

Maldita sea ... me tenía emocionado allí ... busqué por todas las referencias oficiales de "clase interna C# 4.0", pero no tuve ninguna ;-(Mucho de personas lo quieren ... –

0

Puede lograr esto con Mocking en .NET. Sin embargo, no hay soporte en idioma para esta característica, creo que estará disponible en C# 4.0. Hay una serie de librerías que hay para que imita, incluyendo:

1

Esto no es compatible en C#, y si fuera por mí, no debe ser tan ya sea.

La proliferación de clases internas en Java se debe principalmente a la falta de delegados o lambdas, que C# tiene. Entonces, si bien este tipo de funcionalidad actualmente es "su única esperanza" en Java, generalmente puede usar otros mecanismos en C# para lograr los mismos fines. A Java le gusta tocar el piano con una mano en este sentido.

(Es cierto que muchos de nosotros hemos conseguido bastante bueno en este juego con una sola mano, y ahora parece que tenemos que esperar al menos hasta java 8 para cierres ...)

+1

Esto no es una solución. En lugar de decir que no se puede hacer, use X en su lugar, debe mostrarnos cómo usar X para lograr lo mismo. – edthethird

10

Normalmente, los problemas que se resuelven con clases internas anónimas en Java se resuelven de una manera mucho más limpia usando delegados en .Net. Su ejemplo es un poco demasiado simplista para determinar su intención.Si su intención al usar la clase abstracta es pasar un "comportamiento", piense simplemente en usar un delegado Action.

public class StartHere{ 
    public static void main(string[] args){ 
     Action doStuff =() => Console.WriteLine("Did stuff"); 
     executeSomething(doStuff); 
    } 

    public static void executeSomething(Action action) 
    { 
     action(); 
    } 
} 
1

Mientras que todas las buenas respuestas, la mayoría de los arounds de trabajo sugeridas se basan en C# 3.0

Por lo tanto, en aras de la exhaustividad, voy a añadir otra solución que no utiliza ninguno de lambdas ni tipo Func (sentado que , como mencionó Matt Olenik en los comentarios, se podría generalizar a los delegados siguientes para que trabajen de la misma manera). Para aquellos, como yo, que todavía pueden estar trabajando con C# 2.0. Tal vez no sea la mejor solución, pero funciona.

public class Example 
{ 
    public delegate void DoStuffDelecate(); 
    public DoStuffDelecate DoStuff; 
    public delegate void DoStuffWithDelecate(int n); 
    public DoStuffWithDelecate DoStuffWithParameter; 
    public delegate int DoStuffWithReturnDelecate(); 
    public DoStuffWithReturnDelecate DoStuffWithReturnValue; 
} 

class Program 
{ 
    static int MethodWithReturnValue() 
    { 
     return 99; 
    } 
    static void MethodForDelecate() 
    { 
     Console.WriteLine("Did Stuff"); 
    } 
    static void MethodForDelecate(int n) 
    { 
     Console.WriteLine("Did Stuff with parameter " + n); 
    } 


    static void Main(string[] args) 
    { 
     var x = new Example(); 
     x.DoStuff = MethodForDelecate; 
     x.DoStuffWithParameter = MethodForDelecate; 
     x.DoStuffWithReturnValue = MethodWithReturnValue; 

     x.DoStuff(); 
     x.DoStuffWithParameter(10); 
     int value = x.DoStuffWithReturnValue(); 
     Console.WriteLine("Return value " + value); 
     Console.ReadLine(); 
    } 
} 
+0

-1 para obtener información incorrecta sobre las expresiones lambda. No requieren .NET 3.5, solo C# 3.0. El compilador C# 3.0 puede apuntar a .NET 2.0. Las expresiones Lambda son solo azúcares sintácticos para los delegados. Y aunque el tipo Func no existe fuera de System.Core, puede definir fácilmente un equivalente. –

+0

Derecha. Espero que esto sea más exacto. Estoy trabajando en un proyecto ASP.NET y no he encontrado una manera de definir por separado el Framework utilizado y el compilador de C# utilizado, por lo que la respuesta anterior fue todo lo que pude conjurar con mi propia documentación oficial y de datos. – vipirtti

1

Dado que la clase representa sólo una acción, se puede utilizar un delegado en su caso, no hay un delegado existente:

public delegate void Action(); 

Esto es el equivalente exacto de su clase.

y la declaración de la clase anónima es aún más limpio:

Action action =() => Console.WriteLine("Hello world"); 
action(); // invoke 

incluso se puede utilizar de cierre:

public void Hello(string name) 
{ 
    Action action =() => Console.WriteLine("Hello " + name); 
    action(); // will call the above lambda ! 
} 
Cuestiones relacionadas