web-dev-qa-db-ja.com

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

匿名型にインターフェースを実装させることは可能ですか。作業したいコードがありますが、その方法がわかりません。

私は、ノーと言うか、インターフェースを実装するクラスを作成して、その新しいインスタンスを作成するという答えをいくつか持っています。これは実際には理想的ではありませんが、これを単純にするインターフェースの上に薄い動的クラスを作成するためのメカニズムがあるかどうか私は思っています。

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

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

426
Nick Randell

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

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

329
SpaceghostAli

これは2年前の質問かもしれませんが、スレッド内の答えはすべて十分に真実ですが、実際にそれが可能であることを伝えるという衝動に抵抗することはできません。匿名クラスは、そこにたどり着くには少しクリエイティブな不正行為が必要ですが、インターフェースを実装します。

2008年に私は当時の雇用者のためにカスタムLINQプロバイダを書いていました、そしてある時点で私は他の匿名クラスから "私"の匿名クラスを伝えることができる必要がありました。それら。私たちがそれを解決する方法は、ILに直接インターフェースの実装を追加するために、アスペクトを使って( PostSharp を使った)使用することでした。そのため、実際には、匿名クラスにインターフェースを実装させることができます。そこに到達するには、規則を少し曲げる必要があります。

84
Mia Clarke

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

それを回避する最善の解決策は、実装を作成する何らかのタイプの動的プロキシを用意することです。優れた 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>();
42
Arne Claassen

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

このシナリオをサポートするために 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
Jason Bowers

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

12
Marc Gravell

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

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

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

11
ICR

具体的に尋ねられた質問に対する答えはノーです。しかし、あなたはモックフレームワークを見てきましたか?私は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);
}
7
Nine Tails

もう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を取り込んでインターフェースを実装するクラスを1つだけ持つほうが簡単です。

しかし、多くの型をDummyInterfaceに変換する必要がある場合、これははるかに少ない定型句です。

0
Gordon Bean