2009-02-05 4 views
24

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

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#, и если да, как?

ответ

16

С Ламба выражений и класса инициализаторы вы можете получить такое же поведение с небольшим усилием.

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

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

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

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

+2

Это ** не * * то же поведение. Наиболее полезной частью анонимных внутренних классов является возможность быстрого расширения класса без необходимости определения всего нового типа. – makhdumi

8

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

Стоит отметить, что внутренний 'частного class' в C# будет соответствовать Java в' закрытый статический класс класса класса class и, следовательно, не может обращаться к членам класса. –

+0

@ Jean-PhilippePellet это неправда; 'private class' в C# * имеет * доступ к * членам * содержащего класса (даже' private' членов). То, что ему не хватает, является * экземпляром * содержащего класса. Если экземпляр доступен для вложенного класса, вложенный класс имеет полный доступ к членам. –

+0

ОК, спасибо за разъяснение; Я сформулировал свое предложение (и использовал слово «доступ») плохо! –

0

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

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

+0

Я ничего не знаю о C# 4.0 с этими характеристиками. –

+0

Это было динамическое ключевое слово, о котором я думал –

+0

Darn ... вы меня там возбудили ... Я искал высоко и низко для любых официальных ссылок на «внутренний класс C# 4.0», но ни один из них не был ;-(Plenty людей хотят этого, хотя ... –

0

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

1

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

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

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

+1

Это не решение. Вместо того, чтобы говорить, что это невозможно, используйте X вместо этого, вы должны показать нам, как использовать X для достижения того же самого. – edthethird

10

Как правило, проблемы, которые решаются с анонимными внутренними классами 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(); 
    } 
} 
1

Хотя все хорошие ответы, большинство работ обходные предложенных полагаться на 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(); 
    } 
} 
+0

-1 для неправильной информации о лямбда-выражениях. Они не требуют только .NET 3.5, только C# 3.0. Компилятор C# 3.0 может ориентироваться на .NET 2.0. Лямбда-выражения - это только синтаксический сахар для делегатов. И хотя тип Func не существует вне System.Core, вы можете легко определить эквивалент. –

+0

Право. Надеюсь, это более точно. Я сам работаю над проектом ASP.NET, и я не нашел способ отдельно определить используемую Framework и используемый компилятор C#, поэтому предыдущий ответ был всем, что я мог вызвать в воображении со своей собственной иллюстрацией и официальной документацией. – vipirtti

1

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

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