22

オブジェクトを格納するためにジェネリック メソッドを呼び出す場合、特定の型を別の方法で処理する必要がある場合があります。制約に基づいてオーバーロードできないことは知っていますが、他の代替手段には独自の問題があるようです。

public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }

私がやりたいことは、次のようなものです。

public bool Save<SpecificClass>(T entity)
{ ... special logic ... }

過去に、私たちのチームは、これらのクラスを保存するための「1 回限りの」メソッドを次のように作成しました。

public bool SaveSpecificClass(SpecificClass sc)
{ ... special logic ... }

ただし、その関数が存在することを知らず、ジェネリック (保存) を使用しようとすると、「1 回限り」で修正されるはずだった多くの問題に遭遇する可能性があります。これは、新しい開発者がやって来て、ジェネリックの問題を見て、自分の1 回限りの関数でそれを修正しようと決心した場合、さらに悪化する可能性があります。

そう...

この一見一般的な問題を回避するためのオプションは何ですか?

私は UnitOfWork を調べて使用しましたが、実際に問題を解決する唯一のオプションのようですが、ハンマーでハエを攻撃しているようです。

4

4 に答える 4

23

あなたができる:

public bool Save<T>(T entity) where T : class
{ ... some storage logic ... }

public bool Save(SpecificClass entity)
{ ... special logic ... }

例えば:

public class SpecificClass
{
}

public class Specializer
{
    public bool GenericCalled;
    public bool SpecializedCalled;

    public bool Save<T>(T entity) where T : class
    {
        GenericCalled = true;
        return true;
    }

    public bool Save(SpecificClass entity)
    {
        SpecializedCalled = true;
        return true;
    }
}

public class Tests
{
    [Test]
    public void TestSpecialization()
    {
        var x = new Specializer();
        x.Save(new SpecificClass());
        Assert.IsTrue(x.SpecializedCalled);
        Assert.IsFalse(x.GenericCalled);
    }
}
于 2013-03-20T13:44:15.277 に答える
6

基本的に、C# ではテンプレートの特殊化は許可されませんが、次のような継承による場合を除きます。

interface IFoo<T> { }
class Bar { }

class FooBar : IFoo<Bar> { }

少なくとも、コンパイル時にはこれをサポートしていません。ただし、RTTI を使用して、達成しようとしていることを行うことができます。

public bool Save<T>(T entity)
{
    // Check if "entity" is of type "SpecificClass"
    if (entity is SpecificClass)
    {
        // Entity can be safely casted to "SpecificClass"
        return SaveSpecificClass((SpecificClass)entity);
    }

    // ... other cases ...
}

このisは、実行時の型チェックを行うのに非常に便利です。次のコードと同様に機能します。

if (entity.GetType() == typeof(SpecificClass))
    // ...

編集: 未知の型が次のパターンを使用するのはかなり一般的です:

if (entity is Foo)
    return DoSomethingWithFoo((Foo)entity);
else if (entity is Bar)
    return DoSomethingWithBar((Bar)entity);
else
    throw new NotSupportedException(
        String.Format("\"{0}\" is not a supported type for this method.", entity.GetType()));

EDIT 2:他の回答がメソッドをオーバーロードSpecializedClassすることを示唆しているように、ポリモーフィズムを使用している場合は注意が必要です。リポジトリにインターフェイスを使用している場合 (これは、実際にはリポジトリ パターンを設計するための良い方法です)、オーバーロードによって間違ったメソッド get が呼び出される場合がありますSpecializedClass。インターフェース:

interface IRepository
{
    bool Save<T>(T entity)
        where T : class;
}

class FooRepository : IRepository
{
    bool Save<T>(T entity)
    {
    }

    bool Save(Foo entity)
    {
    }
}

これは、次FooRepository.Saveのインスタンスで直接呼び出すと機能しますFoo

var repository = new FooRepository();
repository.Save(new Foo());

ただし、インターフェイスを呼び出している場合 (たとえば、パターンを使用してリポジトリ作成を実装している場合)、これは機能しません。

IRepository repository = GetRepository<FooRepository>();
repository.Save(new Foo());  // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)!

RTTI を使用する方法は 1 つしかなくSave、問題ありません。

于 2013-03-20T14:15:08.867 に答える
3

ジェネリックを含む関数と演算子のオーバーロードは、実行時ではなくコンパイル時にバインドされるため、コードに次の 2 つのメソッドがある場合:

public bool Save<T>(T entity) ...
public bool Save(SomeClass entity) ...

Save(Foo)その場合、ジェネリック型の変数where isを呼び出そうとするコードFooは、ジェネリック型がたまたま であっても、常に前のオーバーロードを呼び出しますSomeClass。それを解決するための私の提案はISaver<in T>、非ジェネリック メソッドでジェネリック インターフェイスを定義することDoSave(T param)です。メソッドを提供するクラスに、Save処理できる型の適切なジェネリック インターフェイスをすべて実装させます。次に、オブジェクトのSave<T>メソッドに へのキャストthisを試みさせISaver<T>ます。キャストが成功した場合は、結果のISaver<T>;を使用します。それ以外の場合は、一般的な保存を実行します。クラスの型宣言に、保存できる型の適切なインターフェイスがすべてリストされている場合、このアプローチはSave呼び出しを適切なメソッドにディスパッチします。

于 2013-03-20T19:30:02.693 に答える
-1

メソッドに異なる名前を使用するのはなぜですか?

以下を参照してください。

    public class Entity
    {
    }

    public class SpecificEntity : Entity
    {
    }

    public class Program
    {
        public static void Save<T>(T entity)
            where T : class
        {
            Console.WriteLine(entity.GetType().FullName);
        }

        public static void Save(SpecificEntity entity)
        {
            Console.WriteLine(entity.GetType().FullName);
        }

        private static void Main(string[] args)
        {
            Save(new Entity());          // ConsoleApplication13.Entity
            Save(new SpecificEntity());  // ConsoleApplication13.SpecificEntity

            Console.ReadKey();
        }
    }
于 2013-03-20T13:44:47.977 に答える