2009-02-05 10 views
24

Je sais que cela peut être fait en Java, car j'ai beaucoup utilisé cette technique par le passé. Un exemple en Java serait montré ci-dessous. (Question supplémentaire. Qu'est-ce que cette technique appelée? Il est difficile de trouver un exemple sans nom.)C#: Création d'une instance d'une classe abstraite sans définir une nouvelle 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(); 
    } 
} 

Maintenant, ma question principale serait, peut-il être fait aussi en C#, et si oui, Comment?

Répondre

16

Avec des expressions Lamba et classe initialiseurs vous pouvez obtenir le même comportement avec un peu d'effort.

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 problème avec cette solution que je viens de réaliser est que si vous deviez créer des champs dans la classe exemple, les expressions lambda ne seraient pas en mesure d'accéder à ces champs.

Cependant, il n'y a aucune raison que vous ne puissiez pas passer l'instance de Example aux expressions lambda qui leur donnerait accès à n'importe quel état public que cet exemple pourrait contenir. AFAIK qui serait fonctionnellement équivalent à la classe interne anonyme de Java.

P.S. Si vous allez voter une réponse, faites-nous une faveur et ajoutez un commentaire sur les raisons pour lesquelles vous n'êtes pas d'accord :-)

+2

* Le même comportement.La partie la plus utile à propos des classes internes anonymes est de pouvoir étendre rapidement une classe sans avoir à définir un nouveau type entier – makhdumi

8

Cela ne peut pas être fait en C#; vous devez déclarer un nouveau type de classe. Le plus proche que vous pouvez obtenir en C# est probablement une classe imbriquée nommée:

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

Il convient de mentionner qu'un 'class' intérieure privée en C# égalerait Java de' classe interne private class, et ne peut donc pas accéder aux membres de la classe conteneur. –

+0

@ Jean-Philippe Pellet n'est pas vrai; une classe privée en C# * a * accès aux * membres * de la classe contenant (même les membres 'private'). Ce qui lui manque est une * instance * de la classe contenant. Si une instance est mise à la disposition de la classe imbriquée, la classe imbriquée a un accès complet aux membres. –

+0

OK, merci pour la clarification; J'ai rédigé ma phrase (et utilisé le mot "accès") mal! –

0

En bref non, vous devez définir comme sous-classe distincte. Je pense que cette fonctionnalité arrive C# 4.0 cependant?

Editer: Non ça ne vient pas C# 4.0 J'ai fait ça.

+0

Je ne connais rien de C# 4.0 avec ces caractéristiques. –

+0

C'était le mot-clé dynamique auquel je pensais –

+0

Darn ... vous m'aviez excité là-bas ... J'ai cherché haut et bas pour toutes les références officielles "class class C# 4.0", mais aucune à avoir ;-(Plenty des gens le veulent bien ... –

0

Vous êtes en mesure d'accomplir cela avec Mocking in .NET. Cependant, il n'y a pas de support dans la langue pour cette fonctionnalité, je pense qu'il sera disponible en C# 4.0. Il y a un certain nombre de bibliothèques là-bas pour Mocking, y compris:

1

Ce n'est pas supporté en C#, et si cela ne tenait qu'à moi, il ne devrait pas être non plus.

La prolifération des classes internes dans Java est principalement due à l'absence de délégués ou de lambdas, ce qui a C#. Donc, alors que ce type de fonctionnalité est actuellement "votre seul espoir" dans Java, vous pouvez généralement utiliser d'autres mécanismes en C# pour atteindre les mêmes fins. Java a l'impression de jouer du piano d'une main à cet égard.

(Il est vrai que beaucoup d'entre nous ont obtenu assez bon à ce jeu une seule main, et maintenant il semble que nous devons attendre au moins jusqu'à ce que java 8 pour les fermetures ...)

+1

Ce n'est pas une solution, au lieu de dire que cela ne peut pas être fait, utilisez X à la place, vous devriez nous montrer comment utiliser X pour accomplir la même chose: – edthethird

10

Généralement, les problèmes résolus avec des classes internes anonymes en Java sont résolus d'une manière beaucoup plus propre en utilisant des délégués dans .Net. Votre exemple est un peu trop simpliste pour déterminer votre intention.Si votre intention en utilisant la classe abstraite est de contourner un "comportement" pensez plutôt à utiliser un délégué Action à la place.

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

Alors que toutes les bonnes réponses, la plupart des contournements de travail proposées se fondent sur C# 3.0

Ainsi, par souci d'exhaustivité, je vais ajouter une autre solution qui utilise ni lambdas, ni le type Func (acquis que , comme Matt Olenik l'a mentionné dans les commentaires, on pourrait généraliser les délégués ci-dessous pour travailler de la même manière.). Pour ceux qui, comme moi, travaillent encore avec C# 2.0. Peut-être pas la meilleure solution, mais cela fonctionne.

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 pour des informations incorrectes sur les expressions lambda.Ils ne nécessitent pas .NET 3.5, seulement C# 3.0 Le compilateur C# 3.0 peut cibler .NET 2.0 Les expressions lambda ne sont que du sucre syntaxique pour les délégués Et bien que le type Func n'existe pas en dehors de System.Core, vous pouvez facilement définir un équivalent: –

+0

Right. J'espère que c'est plus précis. Je travaille sur un projet ASP.NET moi-même, et je n'ai pas trouvé de moyen de définir séparément le Framework utilisé et le compilateur C# utilisé, donc la réponse précédente était tout ce que je pouvais évoquer avec ma propre documentation évidente. – vipirtti

1

Étant donné que votre classe ne représente une action, vous pouvez utiliser un délégué dans votre cas, il y a un délégué existant:

public delegate void Action(); 

Ceci est l'équivalent exact de votre classe.

et la déclaration de votre classe anonyme est encore plus propre:

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

vous pouvez même utiliser la fermeture:

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