C#: создание экземпляра абстрактного класса без определения нового класса

Я знаю, что это можно сделать на 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 ответов


с выражениями 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, который был бы функционально эквивалентен анонимному внутреннему классу Java.

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


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


Как правило, проблемы, которые решаются с анонимными внутренними классами в Java, решаются гораздо более чистым способом с помощью делегатов .Сеть. Ваш пример слишком упрощен, чтобы определить ваше намерение. Если ваше намерение с помощью абстрактного класса состоит в том, чтобы передать "поведение", подумайте только об использовании делегат.

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

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

Это невозможно сделать в 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();
   }
}

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

распространение внутренних классов в java в основном связано с отсутствием делегатов или лямбд, которые имеет C#. Поэтому, хотя этот тип функциональности в настоящее время является "вашей единственной надеждой" на java, вы обычно можете использовать другие механизмы на C# для достижения тех же целей. Java чувствует, что играет на пианино одной рукой в этом отношении.

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


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

public delegate void Action();

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

и déclaration вашего анонимного класса еще чище:

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 !
}

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

Итак, для полноты я добавлю еще одно решение, которое не использует ни lambdas, ни тип 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();
    }
}

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


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

Edit: нет, это не придет C# 4.0, я это придумал.