91

C# の専門化はどのように行いますか?

問題を出します。テンプレートの種類がありますが、それが何であるかわかりません。XYZしかし、それが呼び出したいから派生したものかどうかはわかります.alternativeFunc()。優れた方法は、特殊化された関数またはクラスを呼び出してnormalCallreturn.normalFunc()を持ちながら、派生型の他の特殊化XYZを呼び出して呼び出すこと.alternativeFunc()です。これは C# でどのように行われますか?

4

7 に答える 7

90

C# では、特殊化に最も近いのは、より具体的なオーバーロードを使用することです。ただし、これは脆弱であり、考えられるすべての使用法を網羅しているわけではありません。例えば:

void Foo<T>(T value) {Console.WriteLine("General method");}
void Foo(Bar value) {Console.WriteLine("Specialized method");}

ここで、コンパイラがコンパイル時に型を認識している場合、最も具体的な型を選択します。

Bar bar = new Bar();
Foo(bar); // uses the specialized method

でも....

void Test<TSomething>(TSomething value) {
    Foo(value);
}

これはコンパイル時に焼き付けられるFoo<T>ため、 for でも使用します。TSomething=Bar

もう 1 つのアプローチは、ジェネリック メソッド内で型テストを使用することです。ただし、これは通常、お勧めできません。

基本的に、C# は、ポリモーフィズムを除いて、特殊化を使用することを望んでいません。

class SomeBase { public virtual void Foo() {...}}
class Bar : SomeBase { public override void Foo() {...}}

ここでBar.Fooは、常に正しいオーバーライドに解決されます。

于 2009-03-02T04:51:51.847 に答える
65

C++ テンプレートで実行できるテンプレートの特殊化について話していると仮定すると、このような機能は C# では実際には利用できません。これは、C# ジェネリックがコンパイル中に処理されず、ランタイムの機能であるためです。

ただし、C# 3.0 拡張メソッドを使用して同様の効果を得ることができます。MyClass<int>これは、テンプレートの特殊化と同様に、型のみに拡張メソッドを追加する方法を示す例です。ただし、C# コンパイラは常に拡張メソッドよりも標準メソッドを優先するため、これを使用してメソッドの既定の実装を非表示にすることはできないことに注意してください。

class MyClass<T> {
  public int Foo { get { return 10; } }
}
static class MyClassSpecialization {
  public static int Bar(this MyClass<int> cls) {
    return cls.Foo + 20;
  }
}

これで、次のように記述できます。

var cls = new MyClass<int>();
cls.Bar();

特殊化が提供されていない場合に使用されるメソッドのデフォルトのケースが必要な場合は、1 つの汎用Bar拡張メソッドを作成することでうまくいくと思います。

  public static int Bar<T>(this MyClass<T> cls) {
    return cls.Foo + 42;
  }
于 2009-03-02T01:45:28.503 に答える
20

By adding an intermediate class and a dictionary, specialization is possible.

To specialize on T, we create an generic interface, having a method called (e.g.) Apply. For the specific classes that interface is implemented, defining the method Apply specific for that class. This intermediate class is called the traits class.

That traits class can be specified as a parameter in the call of the generic method, which then (of course) always takes the right implementation.

Instead of specifying it manually, the traits class can also be stored in a global IDictionary<System.Type, object>. It can then be looked up and voila, you have real specialization there.

If convenient you can expose it in an extension method.

class MyClass<T>
{
    public string Foo() { return "MyClass"; }
}

interface BaseTraits<T>
{
    string Apply(T cls);
}

class IntTraits : BaseTraits<MyClass<int>>
{
    public string Apply(MyClass<int> cls)
    {
        return cls.Foo() + " i";
    }
}

class DoubleTraits : BaseTraits<MyClass<double>>
{
    public string Apply(MyClass<double> cls)
    {
        return cls.Foo() + " d";
    }
}

// Somewhere in a (static) class:
public static IDictionary<Type, object> register;
register = new Dictionary<Type, object>();
register[typeof(MyClass<int>)] = new IntTraits();
register[typeof(MyClass<double>)] = new DoubleTraits();

public static string Bar<T>(this T obj)
{
    BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>;
    return traits.Apply(obj);
}

var cls1 = new MyClass<int>();
var cls2 = new MyClass<double>();

string id = cls1.Bar();
string dd = cls2.Bar();

See this link to my recent blog and the follow ups for an extensive description and samples.

于 2010-07-26T18:46:07.217 に答える
19

テンプレートの特殊化をシミュレートするパターンも探していました。いくつかの状況で機能する可能性のあるアプローチがいくつかあります。しかし、ケースはどうですか

static void Add<T>(T value1, T value2)
{
    //add the 2 numeric values
}

ステートメントを使用してアクションを選択することが可能if (typeof(T) == typeof(int))です。しかし、単一の仮想関数呼び出しのオーバーヘッドで実際のテンプレートの特殊化をシミュレートするより良い方法があります。

public interface IMath<T>
{
    T Add(T value1, T value2);
}

public class Math<T> : IMath<T>
{
    public static readonly IMath<T> P = Math.P as IMath<T> ?? new Math<T>();

    //default implementation
    T IMath<T>.Add(T value1, T value2)
    {
        throw new NotSupportedException();    
    }
}

class Math : IMath<int>, IMath<double>
{
    public static Math P = new Math();

    //specialized for int
    int IMath<int>.Add(int value1, int value2)
    {
        return value1 + value2;
    }

    //specialized for double
    double IMath<double>.Add(double value1, double value2)
    {
        return value1 + value2;
    }
}

これで、事前にタイプを知らなくても、次のように記述できます。

static T Add<T>(T value1, T value2)
{
    return Math<T>.P.Add(value1, value2);
}

private static void Main(string[] args)
{
    var result1 = Add(1, 2);
    var result2 = Add(1.5, 2.5);

    return;
}

実装された型だけでなく、派生型に対しても特殊化を呼び出す必要がある場合はIn、インターフェイスのパラメーターを使用できます。ただし、この場合、メソッドの戻り値の型をジェネリック型にすることはできなくなりTます。

于 2015-03-31T21:53:49.100 に答える
5

提案された回答のいくつかは、ランタイム型情報を使用しています。コンパイル時のバインドされたメソッド呼び出しよりも本質的に遅いです。

コンパイラは、C++ のように特殊化を強制しません。

C++ と同様の効果を達成するために、通常のコンパイラが完了した後にコードを挿入する方法については、PostSharp を参照することをお勧めします。

于 2011-03-28T18:58:55.270 に答える
-1

型が XYZ から派生しているかどうかをテストしたいだけの場合は、次を使用できます。

theunknownobject.GetType().IsAssignableFrom(typeof(XYZ));

その場合、「theunknownobject」を XYZ にキャストして、alternativeFunc() を次のように呼び出すことができます。

XYZ xyzObject = (XYZ)theunknownobject; 
xyzObject.alternativeFunc();

お役に立てれば。

于 2009-03-02T01:52:38.330 に答える