2012-12-05 7 views
14

私の質問はIs there a reasonable approach to "default" type parameters in C# Generics?に関連していますが、アプローチが機能しない内部ジェネリッククラスを使用しています。一般的なオプションを作ることができますか?特定のクラスにデフォルト設定していますか?

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型の1つに変わる可能性があります。また、 "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_ChangedFooEventArgsを受け入れるでは動作しませんということです。 FooEventArgs<object>が必要です(Foo<object>からの値になるため)Foo.Changedイベントがそれに渡されます。

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>を使用するかもしれません)?

+0

[関連](https://github.com/dotnet/csharplang/issues/253) – Shimmy

答えて

20

正直言って、あなたができることはあまりありません。あなたでき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です。

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

+0

。しかし、あなたが観察しているように、この場合はそれはしません。ありがとう。 –

+0

@ChrisMorgan:はい - 私はそれを側面の問題として持ち出しただけです。私は別のアイデアで答えの始まりを編集しましたが、実際に*好きなものではありません。 –

+0

いいえ、私はそれも好きではありません...よく、明示的に私はそうだと思います。 –

8

私が見つけたもう一つの興味深いことは、同じ名前で異なる署名を持つジェネリッククラスを作成できることです。

class Foo<T> { 

} 

class Foo<T,T> { 

} 

、あなたはどちらかを呼び出すことができ、それらのいずれかが好きは、次のとおりです。

new Foo<string>(); 
new Foo<int,double>(); 
new Foo<string,int>(); 

を、私はちょうど彼らが別の署名を持っているので、同じ名前を持つ両方のクラスにもかかわらず、彼らは共存できると面白いと思いました。タプルのクラスがどのように機能するか

私はこれがされて推測

インターフェイスの継承を扱うの逆の方法に興味深い洞察だ
関連する問題