匿名クラスはインターフェースを実装できますか?


463

匿名型にインターフェースを実装させることは可能ですか?

機能させたいコードがありますが、その方法がわかりません。

「いいえ」と答えるか、インターフェースを実装するクラスを作成してその新しいインスタンスを構築するかの答えがいくつかありました。これは実際には理想的ではありませんが、インターフェイス上にシンダイナミッククラスを作成して、これを簡単にするメカニズムがあるかどうか疑問に思っています。

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

1つのアプローチを説明する記事「動的インターフェイスのラッピング」を見つけました。これはこれを行う最良の方法ですか?


1
リンクが古くなっているように見えます。これはおそらく適切な代替liensberger.it/web/blog/?p=298です。
Phil Cooper

1
はい、あなたが使用して、(DLR経由).NET 4以降でこれを行うことができますImpromptuInterface nugetパッケージを。
BrainSlugs83 16

1
@PhilCooperあなたのリンクはおそらく2016年以降にダウンしています-しかし幸運にもそれ以前にアーカイブされました。web.archive.org/web/20111105150920/http://www.liensberger.it/...
ポール・

回答:


361

いいえ、匿名型はインターフェースを実装できません。C#プログラミングガイド

匿名型は、1つ以上の読み取り専用のパブリックプロパティで構成されるクラス型です。メソッドやイベントなど、他の種類のクラスメンバーは許可されていません。匿名型は、オブジェクト以外のインターフェイスまたは型にキャストできません。


5
とにかくこれを持っているといいでしょう。コードの読みやすさを考えている場合、ラムダ式は通常は使えません。私たちがRADについて話しているのなら、私はすべてJavaのような匿名インターフェース実装に夢中になっています。ちなみに、一部のケースでは、その機能はデリゲートより強力です
Arsen Zahray

18
@ArsenZahray:ラムダ式を適切に使用すると、コードが読みやすくなります。これらは、関数チェーンで使用される場合に特に強力であり、ローカル変数の必要性を削減または排除できます。
ロイティンカー、

2
-あなたはトリックこうして「 - C#のためのAデザインパターン匿名実装クラス」を行うことができ twistedoakstudios.com/blog/...
ドミトリー・パヴロフ

3
@DmitryPavlov、それは驚くほど貴重でした。通行人: ここに凝縮されたバージョンです。
kdbanman 2015

1
あなたは同じフィールドで匿名オブジェクトに匿名型をキャストすることができますstackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov

89

これは2年前の質問である可能性があり、スレッド内の回答はすべて十分に真実ですが、匿名クラスがインターフェイスを実装すること実際には可能であることをあなたに告げる衝動に抵抗することはできません。そこにたどり着くためのちょっとしたクリエイティブな浮気。

2008年に私は当時の雇用主向けのカスタムLINQプロバイダーを作成していました。ある時点で、「私の」匿名クラスを他の匿名クラスから区別できるようにする必要がありました。つまり、型チェックに使用できるインターフェースを実装する必要がありましたそれら。これを解決する方法は、ILに直接インターフェイス実装を追加するために、アスペクト(PostSharpを使用)を使用することでした。そのため、実際には、匿名クラスにインターフェースを実装させることは可能であり、そこに到達するにはルールを少し曲げるだけで済みます。


8
@Gusdor、この場合、私たちはビルドを完全に制御し、それは常に専用マシンで実行されました。また、PostSharpを使用していて、そのフレームワーク内で完全に合法であるので、使用しているビルドサーバーにPostSharpがインストールされていることを確認している限り、何も起こりません。
Mia Clarke

16
@Gusdor私は、他のプログラマーがプロジェクトを取得して簡単にコンパイルできるはずであることに同意します。VS自体またはC#仕様の一部ではないその他の非標準のMSフレームワークに対しても、同じ議論をすることができます。あなたはそれらのものを必要とするか、そうでなければそれは「ポップ」します。それはIMOの問題ではありません。問題は、ビルドが非常に複雑になり、それらすべてを正しく連携させることが難しい場合です。
AaronLS 2012年

6
@ZainRizviいいえ、そうではありませんでした。私の知る限り、まだ生産中です。提起された懸念は当時私には奇妙に思われ、現在のところ私には恣意的であるように思われました。それは基本的に「フレームワークを使用しないでください、物事は壊れます!」彼らはそうしませんでした、何もポップに行きませんでした、そして私は驚きません。
Mia Clarke

3
今ではずっと簡単です。コードの生成後にILを変更する必要はありません。ImpromptuInterfaceを使用するだけです。-任意のオブジェクト(匿名で型指定されたオブジェクトを含む)を任意のインターフェースにバインドできます(もちろん、クラスが実際にサポートしていないインターフェースの一部を使用しようとすると、遅延バインディングの例外が発生します)。
BrainSlugs83 16

44

匿名型をインターフェースにキャストすることは、私がしばらくの間望んでいたことですが、残念ながら現在の実装では、そのインターフェースの実装が必要になります。

それを回避する最善の解決策は、実装を作成する動的プロキシのタイプを用意することです。あなたが置き換えることができる優れたLinFuプロジェクトを使用して

select new
{
  A = value.A,
  B = value.C + "_" + value.D
};

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();

17
Impromptu-Interface Projectは.NET 4.0でDLRを使用してこれを行い、Linfuよりも軽量です。
jbtule

あるDynamicObjectLinFuタイプは?System.Dynamic.DynamicObject保護されたコンストラクターしかありません(少なくとも.NET 4.5では)。
jdmcnair 2014

はい。私はDynamicObject、DLRバージョンより前のLinFu実装について言及していました
Arne Claassen 14

15

匿名型は、動的プロキシを介してインターフェースを実装できます。

このシナリオをサポートするために、GitHubで拡張メソッドとブログ投稿http://wblo.gs/feEを書きました。

このメソッドは次のように使用できます。

class Program
{
    static void Main(string[] args)
    {
        var developer = new { Name = "Jason Bowers" };

        PrintDeveloperName(developer.DuckCast<IDeveloper>());

        Console.ReadKey();
    }

    private static void PrintDeveloperName(IDeveloper developer)
    {
        Console.WriteLine(developer.Name);
    }
}

public interface IDeveloper
{
    string Name { get; }
}

13

番号; 匿名型は、いくつかのプロパティを除いて何もすることができません。独自のタイプを作成する必要があります。リンクされた記事を詳しくは読んでいませんが、Reflection.Emitを使用して新しいタイプをその場で作成しているようです。ただし、C#自体の中での議論に限定すると、やりたいことができなくなります。


また、重要な点として、プロパティには関数またはボイド(アクション)が含まれる場合があります。selectnew {... MyFunction = new Func <string、bool>(s => value.A == s)}は、参照できませんが機能します関数の新しいプロパティ(「value.A」の代わりに「A」を使用することはできません)。
cfeduke、2008年

2
さて、それはたまたまデリゲートであるプロパティではないですか?実際にはメソッドではありません。
Marc Gravell

1
実行時に型を作成するためにReflection.Emitを使用しましたが、今は実行時のコストを回避するためにAOPソリューションを好むと信じています。
Norman H

11

最善の解決策は、匿名クラスを使用しないことです。

public class Test
{
    class DummyInterfaceImplementor : IDummyInterface
    {
        public string A { get; set; }
        public string B { get; set; }
    }

    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new DummyInterfaceImplementor()
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values.Cast<IDummyInterface>());

    }

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

クエリの結果をインターフェースのタイプにキャストする必要があることに注意してください。それを行うにはもっと良い方法があるかもしれませんが、私はそれを見つけることができませんでした。


2
values.OfType<IDummyInterface>()キャストの代わりに使用できます。実際にその型にキャストできるコレクション内のオブジェクトのみを返します。それはすべてあなたが望むものに依存します。
Kristoffer L

7

具体的に尋ねられた質問への答えはノーです。しかし、あなたはモックフレームワークを見てきましたか?私はMOQを使用していますが、数百万もあり、それらを使用して(部分的または完全に)インターフェースをインラインで実装/スタブできます。例えば。

public void ThisWillWork()
{
    var source = new DummySource[0];
    var mock = new Mock<DummyInterface>();

    mock.SetupProperty(m => m.A, source.Select(s => s.A));
    mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D));

    DoSomethingWithDummyInterface(mock.Object);
}

1

別のオプションは、コンストラクターでラムダをとる単一の具象実装クラスを作成することです。

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

// "Generic" implementing class
public class Dummy : DummyInterface
{
    private readonly Func<string> _getA;
    private readonly Func<string> _getB;

    public Dummy(Func<string> getA, Func<string> getB)
    {
        _getA = getA;
        _getB = getB;
    }

    public string A => _getA();

    public string B => _getB();
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new Dummy // Syntax changes slightly
                     (
                         getA: () => value.A,
                         getB: () => value.C + "_" + value.D
                     );

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

あなたがやろうとしているすべてが変換された場合DummySourceDummyInterface、ちょうど取る一つのクラス持つように単純になりDummySource、コンストラクタおよび実装インタフェースでは。

ただし、多くのタイプをに変換する必要がある場合DummyInterface、これはボイラープレートの数がはるかに少なくなります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.