46

私の質問は、C#ジェネリックの「デフォルト」型パラメーターへの合理的なアプローチはありますか? に関連しています。、しかし、アプローチする内部ジェネリック クラスを使用しても機能しません。

次のようなコードが与えられた場合:

using System;

public class FooEventArgs<T> : EventArgs
{
    // ... T properties and a constructor
}

public class Foo<T>
{
    public delegate void EventHandler<FooEventArgs>(object sender, FooEventArgs<T> e);
    public event EventHandler<FooEventArgs<T>> Changed
}

そして、それは次のように使用されます:

public class User
{
    public Foo<int> foo1;
    public Foo<object> foo2;

    public User()
    {
        foo1 = new Foo<int>();
        foo2 = new Foo<object>();
        foo1.Changed += foo1_Changed;
        foo2.Changed += foo2_Changed;
    }

    protected void foo1_Changed(object sender, FooEventArgs<int> e) { ... }
    protected void foo2_Changed(object sender, FooEventArgs<object> e) { ... }
}

まあ、ジェネリックオプショナルがあればいいのですが、何かが入ってくるタイプがわからない場合が多いので。 (データは、独自の変数タイプを持つ外部システムから来ています。 、その後 .NET 型に変換されますが、たとえば、1 つのリモート データ型がいくつかの .NET 型のいずれかに変わる場合や、それが "any" 型である場合など、次のような状況に遭遇しますobject。その場合の唯一の本当の答えです。)

私がすぐに思いついた解決策はサブクラス化でした(これは、以前にリンクされた質問の主な提案でもありました):

public class Foo : Foo<object>
{
    public Foo(...) : base(...) { }
}

public class FooEventArgs : FooEventArgs<object>
{
    public Foo(...) : base(...) { }
}

次に、次のように使用します。

public class User
{
    public Foo foo3;

    public User()
    {
        foo3 = new Foo();
        foo3.Changed += foo3_Changed;
    }

    protected void foo3_Changed(object sender, FooEventArgs e) { ... }
}

問題は、それが自然にfoo3_Changed受け入れられないことFooEventArgsです; が必要です。これはFooEventArgs<object>Foo.Changedイベントが渡されるものです (値は から取得されるためFoo<object>)。

Foo.cs(3,1415926): error CS0123: No overload for 'foo3_Changed' matches delegate 'FooLibrary.Foo<object>.EventHandler<FooLibrary.FooEventArgs<object>>'

クラスの大部分を複製する以外に、これについてできることはありますか?

もう 1 つ試してみました: から に変換する暗黙の演算子FooEventArgs<object>ですFooEventArgs

    public static implicit operator FooEventArgs(FooEventArgs<object> e)
    {
        return new FooEventArgs(...);
    }

残念ながら、これは機能していないようですが、理由はよくわかりません。

EditBuffer.cs(13,37): error CS0553: 'FooLibrary.FooEventArgs.implicit operator FooLibrary.FooEventArgs(FooLibrary.FooEventArgs<object>)': user-defined conversions to or from a base class are not allowed

それで、もう一度、これについて私にできることはありますか、それともそれはタフラックであり、使用するだけで満足する必要があると考えるのは正しいですFooEventArgs<object>か(そして、使用するだけでよいと思いますFoo<object>)?

4

2 に答える 2

47

正直なところ、それについてできることはあまりないと思います。二重にジェネリックにすることができます:Foo

public class Foo<TData, TArgs> where TArgs : FooEventArgs<TData>
{
    public delegate void EventHandler<TArgs>(object sender, TArgs e);
    public event EventHandler<TArgs> Changed;
}

次に、次のように記述できます。

public class Foo : Foo<object, FooEventArgs>

...しかし、実際には、ほとんどメリットがないため、物事が非常に複雑になっています。

また、型引数を含めると少し冗長になりますが、非常に明確になりますが、継承はさまざまな方法で水を濁らせる可能性があります。動作の特殊化を実際にモデル化しようとしていない場合は、クラスの継承を避けたいと思います。

ちなみに、暗黙的な変換が機能しない理由はジェネリックとは関係ありません-エラーメッセージが示すように、継承階層を上下する変換(暗黙的または明示的)を宣言することはできません。C# 仕様セクション 6.4.1 から:

C# では、特定のユーザー定義の変換のみを宣言できます。特に、既存の暗黙的または明示的な変換を再定義することはできません。

(詳細については、そのセクションを参照してください。)


余談ですが、一般的にはインターフェイスを使用して、ジェネリックに対して逆の方法で継承を使用することがより一般的であることがわかりました。

public interface IFoo
{
    // Members which don't depend on the type parameter
}

public interface IFoo<T> : IFoo
{
    // Members which all use T
}

IFooそうすれば、コードは を知る必要がない場合、ジェネリック側を気にせずにだけを受け取ることができますT

残念ながら、それはあなたの特定のケースでは役に立ちません。

于 2012-12-05T06:54:08.733 に答える