2

私はC#を初めて使用し、現在EntityFrameworkを使用してアプリケーションを開発しています。データベースコンテキストクラスの機能を拡張して、メソッドgetPool()を呼び出して、クラスの対応するDbSetメンバーを渡すことができるようにします。

後で他のテンプレートから呼び出されるため、テンプレートとして実装する必要があります。他のテンプレートは、(グローバル)データベースコンテキストオブジェクトと、データベースにクエリを実行するタイプT(特定のスーパークラスを持つ)について知っているだけです。

これが私が試したものです(少し単純化されています-元の例は複雑すぎます):

public class TestContext : DbContext
{
    public DbSet<TestA> ATests { get; set; }
    public DbSet<TestB> BTests { get; set; }

    public IQueryable<T> getPool<T>() where T : TestA {
       return (IQueryable<T>)ATests;
    }
    public IQueryable<T> getPool<T>() where T : TestB {
       return (IQueryable<T>)BTests;
    }
}

エラーメッセージは

エラー:タイプ「...」は、同じパラメータータイプを持つ「...」というメンバーをすでに定義しています

そして、それは私のテンプレートの2番目の特殊な定義の行で発生します(public IQueryable<T> getPool<T>() where T : TestB)。

問題は、これをどのように修正するかです。

4

2 に答える 2

5

残念ながら、C#では、このようなジェネリック型制約を使用してメソッドをオーバーロードすることはできません。あなたは彼らにこのような異なる名前を付ける必要があります

public class TestContext : DbContext
{
    public DbSet<TestA> ATests { get; set; }
    public DbSet<TestB> BTests { get; set; }

    public IQueryable<T> getPoolA<T>() where T : TestA {
       return (IQueryable<T>)ATests;
    }
    public IQueryable<T> getPoolB<T>() where T : TestB {
       return (IQueryable<T>)BTests;
    }
}

別の解決策は、次のようなことを行うことです。

public class TestContext : DbContext
{
    public DbSet<TestA> ATests { get; set; }
    public DbSet<TestB> BTests { get; set; }

    public IQueryable<T> getPool<T>() {
       return (typeof(T) == typeof(TestA))
                  ? (IQueryable<T>)ATests
                  : (IQueryable<T>)BTests;
    }
}

これで、これをさらにクリーンにすることができます。IQueryable<T>はで共変でTあるため、キャストを回避できます。

public class TestContext : DbContext
{
    public DbSet<TestA> ATests { get; set; }
    public DbSet<TestB> BTests { get; set; }

    public IQueryable<T> getPool<T>() {
       return (typeof(T) == typeof(TestA)) ? ATests : BTests;
    }
}

タイプのテストを避けたい場合は、次のようにすることができます。

public class TestContext : DbContext
{
    readonly Dictionary<Type, object> _sets;

    public DbSet<TestA> ATests { get; set; }
    public DbSet<TestB> BTests { get; set; }

    public TestContext()
    {
        _sets = new Dictionary<Type, object>
        {
            { typeof(TestA), ATests },
            { typeof(TestB), BTests }
        }
    }

    public IQueryable<T> getPool<T>() {
       return (IQueryable<T>)_sets[typeof(T)];
    }
}
于 2012-10-18T17:08:23.817 に答える
1

(ジェネリックスとC#全般について読む以外に)実行時に目的のタイプでプールを構成し、それらを辞書に保存して、Typeキーとして使用することをお勧めします。つまり、次の行に沿ったものです...:

//...

// configuration, maybe factor out to a dedicated class...
private readonly IDictionary<System.Type, IQueryable> m_SupportedPools =
    new Dictionary<System.Type, IQueryable>();

// add this queryable, note that type inference works here
public void AddToPool<T>(IQueryable<T> p_Queryable)
{
    m_SupportedPools.Add(typeof(T), p_Queryable);
}

public IQueryable<T> GetPool<T>()
{
    IQueryable t_Set = null;  
    if (m_SupportedQueries.TryGetValue(typeof(T), out t_Set)) {
        return t_Set as IQueryable<T>;
    } else {
        return null;
    }
}
于 2012-10-18T17:40:26.210 に答える