web-dev-qa-db-ja.com

インターフェイスに「適合する」が、実際には実装しないC#パラメーターを渡す

注:これは実際にはひどいアイデアです。CLRで何ができるのか、「クラスを作成してからクラスを変更する」プリプロセッサを作成することを目標にしています。 =

次のクラスがあるとします。このクラスは別のアセンブリで定義されているため、変更できません。

class Person {
    public string Greet() => "Hello!";
}

次のように、インターフェイスとメソッドを定義します。

interface IGreetable {
    string Greet();
} 

// ...

void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());

クラスPersonは明示的にIGreetableを実装しませんが、メソッドを変更せずにcouldを実装します。

それで、上記のコードを変更せずにPersonのインスタンスをPrintGreetingに正常に渡すことができるReflection、DLRなどを使用する方法はありますか?

33

ライブラリを使用してみてください Impromptu-Interface

[Impromptu-Interface]フレームワークにより、オブジェクトを継承していなくても、静的インターフェイスでオブジェクト(静的または動的)をラップできます。 これは、プロキシ内でキャッシュされた動的バインディングコードを発行することで行われます。

これにより、次のようなことができます。

var person = new Person();
var greeter = person.ActLike<IGreetable>();
29
Roy Sanchez

dynamicラッパーオブジェクトを使用して自分でこれを結び付けることもできますが、ラッピングクラス内の型の安全性が失われます。

class GreetableWrapper : IGreetable
{
    private dynamic _wrapped;
    public GreetableWrapper(dynamic wrapped)
    {
        _wrapped = wrapped;
    }

    public string Greet()
    {
        return _wrapped.Greet();
    }
}

static void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
static void Main(string[] args)
{
    PrintGreeting(new GreetableWrapper(new Person()));
    Console.ReadLine();
}
7
John Koerner

これはすぐにかなり簡単になるかもしれません。型クラスは、shapesとしてC#に導入できます。このクラスでは、クラスの機能とそれに対するコードを定義できますshapeそして、コードの作成者が何も宣言することなく、一致する任意のタイプのコードを使用します。

現在、C#で最も近いのは、おそらくGetEnumerator()を持つ型でforeachがどのように動作し、MoveNext()Currentを持つ型のオブジェクトを返すかです。 IEnumerableなどを実装していない場合でも、コンパイラが扱う組み込みの概念である場合にのみ、ここで定義できます。

興味深いことに、静的メンバーを定義することもできます。

6
Jon Hanna

これが可能だとは思わない。コンパイラは、すべてが実装されていることをコンパイラが確認できるように、インターフェイスまたはクラスを明示的に実装するものを確認する必要があります。

リダイレクトを使用してそれを行うことができる場合、何かを実装することに失敗する可能性があります。そして、これは.NETが採用する安全アプローチに反します。

4
Jonathan Wood

外部コードを制御し、オブジェクトをラップすることを望んでいる場合(そして、ここでのすべての回答がラップしているように見えます)、動的バインディングとImpromptu-Interfaceのようなライブラリは、本質的に何かのための多くのトラブルのように思えます一発ギャグ。

class GreetablePerson : Person, IGreetable { }

これで完了です。

コンパイラがGreetablePersonクラスを構築しているとき、Personのメソッドは最終的にインターフェイスの暗黙的な実装を実行し、すべてが「正常に機能します」。唯一のイライラは、外部のコードがGreetablePersonオブジェクトをインスタンス化する必要があることですが、標準のオブジェクト指向の用語では、GreetablePersonのインスタンスisPersonので、これは質問に対する有効な答えのように思えます。

Personの既存のインスタンスも持つように要件が変更された場合、Impromptu-Interfaceのようなものがより魅力的になるかもしれませんが、それでもGreetablePersonにPersonからコピーするコンストラクタを与えることを検討したいかもしれません。そこから最適なパスを選択するには、要件についての詳細と、問題のPersonクラスの実際の実装の詳細を取得する必要があります。

4
GrandOpener

オプションは、個人に対するラッパークラスを作成し、このラッパーをメソッドに渡すことです。ラッパーは、インターフェイスを明示的に実装する必要があります。

4
Mauri

無関係なものではなく、これはScalaやHaskellなど、他の言語で一般的に行われていることです。

「タイプクラス」と呼ばれるものを使用することで知られています。基本的に、型クラスを使用すると、実際にインターフェイスを実装する必要なく、インターフェイスを明示的に実装するかのように、型の動作を定義できます。それについてもっと読むことができます こちら

1
wheeler