0

それぞれ独自の名前空間を持つ 2 つのライブラリが提供されています。API の要求部分のクラス構造は同じですが、応答は異なります。

名前空間の種類に関係なく、両方の要求を処理できる関数を作成したいと考えています (ジェネリックを試してみましたが、解決策を作成できませんでした)。

これが私がやろうとしていることの例です:

public void Function1()
{
    Namespace1.MyType type1 = new Namespace1.MyType1();
    type1.InnerType = new Namespace1.MyType2();

    type1.PerformMethod1();
}

public void Function2()
{
    Namespace2.MyType type1 = new Namespace2.MyType1();
    type1.InnerType = new Namespace2.MyType2();

    type1.PerformMethod1();
}

次の方法で、メソッド レベルでジェネリックを使用しようとしました。

public void Function1<TType1, TType2>() 
    where TType1: NamesSpace1.Type1, NamesSpace2.Type1, new() 
    where TType2: Namespace1.Type2. Namespace2.Type2(), new()
{
    TType1 type1 = new TType1();
    type1.InnerType = new TType2();

    type1.PerformMethod1();
}

ただし、.net では、ジェネリックに対するこのレベルの制約は許可されていません。ジェネリック関数を作成する方法についての提案は素晴らしいでしょう。

ありがとう

4

2 に答える 2

2

簡単に言えば、できません。

C# (または .NET) は "構造サブタイピング"をサポートしていないため、これら 2 つのクラスは完全に分離されていますが、メンバーは同じです。

ただし、キーワードを使用することはできdynamicますが、コンパイル時の型の安全性がすべて失われます。

publiv void Function(dynamic type1)
{
    type1.PerformMethod1();
}

と の 2 つの異なるインスタンスを作成する必要があるtype1ためtype2、ここではあまりメリットがありません。

基本的に同じ欠点を持つ別の可能な解決策は、リフレクションを使用して型を作成することです。

public void Function(string assemblyName, string namespace)
{
    var type1Name = namespace + ".Type1, " + assemblyName;
    var type2Name = namespace + ".Type2, " + assemblyName;
    dynamic type1 = Activator.CreateInstance(Type.GetType(type1Name));
    dynamic type2 = Activator.CreateInstance(Type.GetType(type1Name));
    type1.InnerType = type2;
    type1.PerformMethod1();
}

3 番目の解決策は、ジェネリックのみをnew()制約付きで使用して、インスタンスをより簡単に作成し、実行時の型チェックを使用して、許可された型のみが使用されていることを確認することです。繰り返しますが、これには他の 2 つのソリューションと同じ欠点があります: コンパイル時の型の安全性がない:

public void Function1<TType1, TType2>() 
    where TType1 : new() 
    where TType2 : new()
{
    if(typeof(TType1) != typeof(Namespace1.MyType1) || 
       typeof(TType1) != typeof(Namespace2.MyType1) || 
       typeof(TType2) != typeof(Namespace1.MyType2) || 
       typeof(TType2) != typeof(Namespace2.MyType2))
        throw new ArgumentException(...); 

    // .NET 4:
    dynamic type1 = new TType1();
    dynamic type2 = new TType2();
    // Pre .NET 4:
    // var type1 = new TType1();
    // var type2 = new TType2();

    // .NET 4:
    type1.InnerType = type2;
    // Pre .NET 4:
    // type1.GetType().GetProperty("InnerType").SetValue(type1, type2);

    // .NET 4:
    type1.PerformMethod1();
    // Pre .NET 4:
    // type1.GetType().GetMethod("PerformMethod1").Invoke(type1, new object[0]);
}
于 2013-02-21T11:45:58.490 に答える
1

.Netのジェネリックは、実際にはそのようには機能しません。

これを行うための最良の方法は、共通のインターフェースを作成し、何らかの形でライブラリークラスに適合させることだと思います。問題はそれをどのように行うかです。

問題の型が封印されておらず、そのインスタンスを自分で作成している場合は、このインターフェイスを実装する派生クラスを作成できます。これは、継承されたメソッドによって自動的に実装されます。

interface IMyType
{
    object InnerType { get; set; }
    void PerformMethod1();
}

class Namespace1MyTypeWithInterface : Namespace1.MyType, IMyType
{}

これは、両方のタイプが実際に同一であることを前提としています。たとえば、タイプInnerTypeがそれぞれ異なる場合、状況はより複雑になります。これを解決する1つの方法は、プロパティの型でインターフェイスをジェネリックにすることです。

前のソリューションが機能しない場合は、名前空間ごとにアダプタークラスを作成できます。両方のアダプターは再び同じインターフェースを共有し、各呼び出しを実際のタイプのインスタンスに委任します。

class Namespace1MyTypeAdapter : IMyType
{
    private readonly Namespace1.MyType m_adapted;

    public Namespace1MyTypeAdapter(Namespace1.MyType adapted)
    {
        m_adapted = adapted;
    }

    public object InnerType
    {
        get { return m_adapted.InnerType; }
        set { m_adapted.InnerType = value; }
    }

    public void PerformMethod1()
    {
        m_adapted.PerformMethod1();
    }
}

繰り返されるコードはたくさんありますが、一度書くと、両方のクラスを同じように使用できます。

さらに別のオプションは、使用dynamicまたはリフレクションです。

于 2013-02-21T12:01:07.743 に答える