web-dev-qa-db-ja.com

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#でも実行できるか、もしそうなら、どのように実行できるかということです。

24
vipirtti

ランバ式とクラス初期化子を使用すると、少しの努力で同じ動作を得ることができます。

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

私が今気付いたこのソリューションの問題の1つは、Exampleクラスでフィールドを作成すると、ラムダ式がそれらのフィールドにアクセスできないことです。

ただし、Exampleのインスタンスをラムダ式に渡して、exampleが保持する可能性のあるパブリック状態にアクセスできるようにすることができなかった理由はありません。 Java匿名内部クラスと機能的に同等であるAFAIK。

P.S.反対票を投じる場合は、賛成して、同意しない理由についてコメントを追加してください:-)

17
Darrel Miller

Javaテクニックは " 匿名内部クラス "と呼ばれ、C#には同等のものはありません。

21
Anton Gogolev

通常、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();
   }
}
10
Michael Meadows

これは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();
   }
}
8
Marc Gravell

クラスはアクションのみを表すため、ケースではデリゲートを使用できます。既存のデリゲートがあります。

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

これはC#ではサポートされていません。私次第であれば、サポートされるべきではありません。

Javaでの内部クラスの急増は、主にC#が持つデリゲートまたはラムダの欠如によるものです。したがって、このタイプの機能は現在Javaで「唯一の希望」ですが、通常は可能です。同じ目的を達成するためにC#の他のメカニズムを使用してください。Javaこの点で、片手でピアノを弾くように感じます。

(確かに、私たちの多くはこの片手でのプレイがかなり上手になっています。そして今では、少なくともJava 8が終了するまで待つ必要があるようです...)

1
krosenvold

すべての良い答えがありますが、提案された回避策のほとんどはC#3.0に依存しています

したがって、完全を期すために、ラムダもFunc型も使用しない別のソリューションを追加します(Matt Olenikがコメントで述べたように、以下のデリゲートを一般化して同じように機能させることができます)。私のように、まだ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
vipirtti

要するに、あなたはそれを別個のサブクラスとして定義しなければなりません。この機能はC#4.0で提供されると思いますが?

編集:いいえ、C#4.0は付属していません。

0
Chris S

これは、.NETのモックで実現できます。ただし、この機能の言語サポートはありません。C#4.0で利用できるようになると思います。 Mockingには、次のような多くのライブラリがあります。

0
Nick Berardi