1

いくつかのエンティティ インターフェイスを定義するクラス ライブラリがあるとします。

public interface ISomeEntity { /* ... */ }
public interface ISomeOtherEntity { /* ... */ }

このライブラリは、IRepositoryインターフェースも定義します。

public interface IRepository<TEntity> { /* ... */ }

RepositorySourceBase最後に、ライブラリには(以下を参照)と呼ばれる抽象クラスがあり、これをメイン プロジェクトで実装する必要があります。このクラスの目的は、基本クラスがRepository実行時に新しいオブジェクトを取得できるようにすることです。特定のリポジトリが必要なため (この例ではISomeEntityandのリポジトリ)、メソッドISomeOtherEntityの汎用オーバーロードを記述しようとしています。GetNew<TEntity>()

次の実装はコンパイルされません ( GetNew()where 句が異なっていても、2 番目のメソッドは "既に定義されている" というフラグが付けられます) が、私が達成しようとしていることに到達します。

public abstract class RepositorySourceBase // This doesn't work!
{
    public abstract Repository<TEntity> GetNew<TEntity>()
        where TEntity : SomeEntity;
    public abstract Repository<TEntity> GetNew<TEntity>()
        where TEntity : SomeOtherEntity;
}

このクラスの使用目的は次のようになります。

public class RepositorySourceTester
{
    public RepositorySourceTester(RepositorySourceBase repositorySource)
    {
        var someRepository = repositorySource.GetNew<ISomeEntity>();
        var someOtherRepository = repositorySource.GetNew<ISomeOtherEntity>();
    }
}

一方、メイン プロジェクト (ライブラリ プロジェクトを参照する) には、 と の実装がISomeEntityありISomeOtherEntityます。

public class SomeEntity : ISomeEntity { /* ... */ }
public class SomeOtherEntity : ISomeOtherEntity { /* ... */ }

主なプロジェクトには、次の実装もありますIRepository<TEntity>

public class Repository<TEntity> : IRepository<TEntity>
{
    public Repository(string message) { }
}

そして最も重要なことは、abstract の実装があることRepositorySourceBaseです。

public class RepositorySource : RepositorySourceBase
{
    public override IRepository<ISomeEntity> GetNew()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeEntity>(
            "stuff only I know");
    }

    public override IRepository<ISomeOtherEntity> GetNew()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeOtherEntity>(
            "other stuff only I know");
    }
}

と同様にRepositorySourceBase、2 番目のGetNew()メソッドには「定義済み」のフラグが立てられます。


つまり、C# は基本的に同じメソッドを繰り返していると考えています。なぜなら、メソッドとそのパラメーターだけを区別する方法がないからです。しかし、私の使用例を見るとGetNew()、ジェネリック型と必要なメソッドを区別できるはずです。パラメータ、例:<ISomeEntity>または<ISomeOtherEntity>)。

これを機能させるにはどうすればよいですか?


アップデート

特定の名前のメソッドとFunc<T, TResult>パラメーターを使用してこれを解決することになりました。

したがって、RepositorySourceBase次のようになります。

public abstract class RepositorySourceBase
{
    public abstract Repository<ISomeEntity> GetNewSomeEntity();
    public abstract Repository<ISomeOtherEntity> GetNewSomeOtherEntity();
}

RepositorySourceのようになります。

public class RepositorySource : RepositorySourceBase
{
    public override IRepository<ISomeEntity> GetNewSomeEntity()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeEntity>(
            "stuff only I know");
    }

    public override IRepository<ISomeOtherEntity> GetNewSomeOtherEntity()
    {
        return new (IRepository<ISomeEntity>)Repository<SomeOtherEntity>(
            "other stuff only I know");
    }
}

さて、このすべてが始まったRepositoryUtilizerのは、リポジトリのタイプ (ジェネリック型パラメーターとして指定できる) を知るだけで、ソースからリポジトリを取得できるジェネリック クラスが必要だったからです。結局のところ、それは不可能でした (または、少なくとも簡単には不可能でした)。ただし、デリゲートをパラメーターとして使用して、クラスがメソッド名を「知る」必要なしにリポジトリを取得できるようにすることが可能です。Func<T, TResult>RepositoryUtilizer

次に例を示します。

public class RepositoryUtilizer
{
    public DoSomethingWithRepository<TEntity>(
        Func<TRepositorySource, IRepository<TEntity>> repositoryGetter)
        {
            using (var repository = repositoryGetter(RepositorySource))
            {
                return repository.DoSomething();
            }
        }
    }
}
4

4 に答える 4

2

You cannot get this work as you intended. Type constraints cannot be used to decide between your two methods.

public abstract Repository<TEntity> GetNew<TEntity>()
    where TEntity : SomeEntity;

public abstract Repository<TEntity> GetNew<TEntity>()
    where TEntity : SomeOtherEntity;

Assume

public class SomeEntity { }

public class SomeOtherEntity : SomeEntity { }

and SomeOtherEntity is a valid type argument for both methods yielding two methods with identical signature.

The way to go is probably a single generic method that uses the supplied type argument to dispatch the call to the desired implementation. This is in turn probably solved most easily by implementing an interface on all concrete types.

于 2010-05-14T23:37:29.260 に答える
1

制約は署名の一部ではありません。この事実には多くの影響があり、その多くは人々を際限なく苛立たせているようです。これらの影響のいくつかと、私が間違っていると言っている約 100 万件のコメントについては、この記事とそれに付随するコメントを参照してください。

http://blogs.msdn.com/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

2 つの異なる名前を持つ 2 つのメソッドを用意することで、問題を解決できます。

于 2010-05-15T05:13:10.340 に答える
0

私が考えることができる唯一の解決策はIRepositorySource<T>、各RepositorySourceクラスが明示的に実装できるインターフェースを定義することです。

public interface IRepositorySource<T>
{
    IRepository<T> GetNew();
}

public class RepositorySource : IRepositorySource<ISomeEntity>, IRepositorySource<ISomeOtherEntity>
{
    IRepository<ISomeEntity> IRepositorySource<ISomeEntity>.GetNew()
    {
        ...
    }

    IRepository<ISomeOtherEntity> IRepositorySource<ISomeOtherEntity>.GetNew()
    {
        ...
    }
}

これらのメソッドにアクセスするには、RepositorySourceインスタンスを必要なインターフェイスタイプにキャストする必要があります。

IRepository<IEntity> r = ((IRepositorySource<IEntity>)repositorySource).GetNew();
于 2010-05-14T23:52:48.073 に答える
0

パブリッククラスのリポジトリソース{

static IRepository<T> IRepositorySource.GetNew<T>()

{
    if (typeof(T) == typeof(ISomeEntity))
       return (IRepository<T>)new SomeEntityRepository();
    ...
}

}

于 2010-05-15T00:07:10.303 に答える