2009-02-05 17 views
24

So che può essere eseguita in Java, in quanto ho utilizzato questa tecnica in modo estensivo in passato. Di seguito verrà mostrato un esempio in Java. (Ulteriori domanda. Che cosa è questa tecnica chiamata? E 'difficile trovare un esempio di questo senza un nome.)C#: creazione di un'istanza di una classe astratta senza definizione di nuova classe

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(); 
    } 
} 

Ora, la mia domanda principale sarebbe, questo può anche essere fatto in C#, e in caso affermativo, Come?

risposta

16

Con espressioni Lamba e di classe inizializzatori è possibile ottenere lo stesso comportamento con un po 'di sforzo.

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 questa soluzione che ho capito è che se si dovesse creare campi nella classe di esempio, le espressioni lambda non sarebbe in grado di accedere a tali campi.

Tuttavia, non vi è alcun motivo per cui non è possibile passare l'istanza di Example alle espressioni lambda che consentirebbero loro l'accesso a qualsiasi stato pubblico che l'esempio potrebbe contenere. AFAIK che sarebbe funzionalmente equivalente alla Java Inner Inner Class.

P.S. Se voterai una risposta, fai un favore a tutti e aggiungi un commento sul perché non sei d'accordo :-)

+2

Questo è ** non * * lo stesso comportamento La parte più utile delle classi interne anonime è la possibilità di estendere rapidamente una classe senza dover definire un intero nuovo tipo: – makhdumi

8

Questo non può essere fatto in C#; devi dichiarare un nuovo tipo di classe. Il più vicino si può ottenere in C# è probabilmente una classe annidata nome:

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 ricordare che un interno 'class' privato in C# sarebbe partita Java di' classe interna privata di classe statica, e quindi non può accedere ai membri della classe contenente. –

+0

@ Jean-PhilippePellet non è vero; una 'private class' in C# * * ha accesso ai * membri * della classe contenente (anche membri' private'). Quello che manca è un * istanza * della classe contenente. Se un'istanza è resa disponibile per la classe nidificata, la classe nidificata ha pieno accesso ai membri. –

+0

OK, grazie per i chiarimenti; Ho formulato la mia frase (e usato la parola "accesso") male! –

0

Insomma no, è necessario definire come classe sub separata. Penso che questa funzionalità stia arrivando C# 4.0?

Modifica: No non sta arrivando C# 4.0 L'ho inventato.

+0

Non sono a conoscenza di nulla in C# 4.0 con quelle caratteristiche. –

+0

Era la parola chiave dinamica che stavo pensando di –

+0

Darn ... mi hai eccitato lì ... Ho cercato in alto e in basso per qualsiasi riferimento "interno di classe C# 4.0" ufficiale, ma nessuno da avere ;-(Abbondanza La gente lo vuole però ... –

0

È possibile eseguire questa operazione con Mocking in. NET. Tuttavia non esiste un supporto in lingua per questa funzione, penso che sarà disponibile in C# 4.0. Ci sono una serie di librerie là fuori per Mocking, tra cui:

1

Questo non è supportata in C#, e se fosse per me non dovrebbe essere così o.

La proliferazione delle classi interne in Java è principalmente dovuta alla mancanza di delegati o lambda, che C# ha. Quindi, mentre questo tipo di funzionalità è attualmente "la tua unica speranza" in Java, puoi solitamente utilizzare altri meccanismi in C# per raggiungere gli stessi fini. Java ha l'impressione di suonare il piano con una mano in questo senso.

(È vero che molti di noi hanno ottenuto abbastanza bravo a questo gioco con una sola mano, e ora sembra che dovremo aspettare almeno fino a Java 8 per le chiusure ...)

+1

Questa non è una soluzione, invece di dire che non può essere fatto, usa X invece, dovresti mostrarci come usare X per ottenere la stessa cosa – edthethird

10

In genere, i problemi risolti con le classi interne anonime in Java vengono risolti in modo molto più pulito usando i delegati in .Net. Il tuo esempio è un po 'troppo semplicistico per determinare il tuo intento.Se il tuo intento usando la classe astratta significa passare un "comportamento", pensa solo a usare un delegato 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

Mentre tutte le buone risposte, la maggior parte dei arounds di lavoro proposti si basano su C# 3.0

Così, per amor di completezza, aggiungerò un'altra soluzione che utilizza né lambda né di tipo Func (ammesso che , come Matt Olenik ha menzionato nei commenti, si potrebbe generalizzare i delegati di seguito a lavorare allo stesso modo.). Per quelli come me che potrebbero ancora lavorare con C# 2.0. Forse non è la soluzione migliore, ma funziona.

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 per informazioni errate sulle espressioni lambda. Non richiedono .NET 3.5, solo C# 3.0 Il compilatore C# 3.0 può essere utilizzato come target .NET 2.0 Le espressioni Lambda sono solo zucchero sintattico per i delegati e sebbene il tipo Func non esista al di fuori di System.Core, è possibile definire facilmente un equivalente. –

+0

Right. Spero che questo sia più accurato. Sto lavorando a un progetto ASP.NET da solo, e non ho trovato un modo per definire separatamente il Framework utilizzato e il compilatore C#, quindi la risposta precedente era tutto ciò che potevo evocare con la mia documentazione personale e ufficiale. – vipirtti

1

Dal momento che la classe rappresenta solo un'azione, è possibile utilizzare un delegato nel tuo caso, non v'è un delegato esistente:

public delegate void Action(); 

Questo è l'esatto equivalente della classe.

e la dichiarazione della classe anonima è ancora più pulito:

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

si può anche utilizzare la chiusura:

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