C #: создание экземпляра абстрактного класса без определения нового класса - PullRequest
24 голосов
/ 05 февраля 2009

Я знаю, что это можно сделать на Java, так как в прошлом я довольно широко использовал эту технику. Пример на Java будет показан ниже. (Дополнительный вопрос. Как называется эта техника? Трудно найти пример этого без имени.)

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

Теперь мой главный вопрос: можно ли это сделать в C #, и если да, то как?

Ответы [ 9 ]

21 голосов
/ 05 февраля 2009

Техника Java называется " Анонимный внутренний класс ", и в C # нет эквивалента.

17 голосов
/ 05 февраля 2009

С помощью выражений lamba и инициализаторов классов вы можете получить то же поведение, приложив немного усилий.

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

Одна проблема с этим решением, которое я только что осознал, заключается в том, что если бы вы создали поля в классе Example, лямбда-выражения не могли получить доступ к этим полям.

Однако нет никакой причины, по которой вы не могли бы передать экземпляр Example лямбда-выражениям, которые предоставили бы им доступ к любому общедоступному состоянию, которое может содержать этот пример. AFAIK, который был бы функционально эквивалентен внутреннему классу Anonymous Java.

P.S. Если вы собираетесь проголосовать за ответ ниже, сделайте нам одолжение и добавьте комментарий о том, почему вы не согласны: -)

10 голосов
/ 05 февраля 2009

Как правило, проблемы, которые решаются с помощью анонимных внутренних классов в Java, решаются гораздо чище, с использованием делегатов в .Net. Ваш пример слишком упрощен, чтобы определить ваше намерение. Если ваше намерение с использованием абстрактного класса - передать «поведение», подумайте о том, чтобы просто использовать вместо него делегат 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();
   }
}
8 голосов
/ 05 февраля 2009

Это не может быть сделано в C #; вам нужно объявить новый тип класса. Ближайший, который вы можете получить в C #, это, вероятно, именованный вложенный класс:

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();
   }
}
1 голос
/ 09 февраля 2009

Так как ваш класс представляет только действие, вы можете использовать делегата в вашем случае, существует существующий делегат:

public delegate void Action();

Это точный эквивалент вашего класса.

И декларация вашего анонимного класса еще чище:

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

Вы даже можете использовать замыкание:

public void Hello(string name)
{
  Action action = () => Console.WriteLine("Hello " + name);
  action(); // will call the above lambda !
}
1 голос
/ 06 февраля 2009

Несмотря на все хорошие ответы, большинство предложенных обходных путей полагается на C # 3.0

Итак, для полноты картины я добавлю еще одно решение, которое не использует ни лямбда-выражения, ни тип Func (допустим, что, как упоминал Мэтт Оленик в комментариях, можно обобщить приведенные ниже делегаты для работы таким же образом.). Для таких, как я, которые все еще могут работать с C # 2.0. Возможно, не лучшее решение, но оно работает.

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();
    }
}
1 голос
/ 05 февраля 2009

Это не поддерживается в C #, и если бы это было до меня, это не должно быть так же.

Распространение внутренних классов в Java объясняется главным образом отсутствием делегатов или лямбд, которые есть в C #. Таким образом, хотя этот тип функциональности в настоящее время является «вашей единственной надеждой» в Java, вы обычно можете использовать другие механизмы в C # для достижения тех же целей. В этом смысле Яве хочется играть на пианино одной рукой.

(По общему признанию, многие из нас довольно хорошо справились с этой игрой одной рукой, и теперь нам кажется, что нам нужно подождать, по крайней мере, до 8-й версии для закрытия ...)

0 голосов
/ 05 февраля 2009

Вы можете сделать это с помощью Mocking в .NET. Однако поддержка этой функции в языке отсутствует, думаю, она будет доступна в C # 4.0. Существует множество библиотек для Mocking, в том числе:

0 голосов
/ 05 февраля 2009

Короче говоря нет, вы должны определить его как отдельный подкласс. Я думаю, что эта функция идет C # 4.0, хотя?

Редактировать: Нет, это не будет C # 4.0 Я придумал это.

...