質問

次のことが可能かどうか疑問に思っていました。匿名型(string、int、decimal、customObjectなど)を受け入れるクラスを作成し、Typeに基づいてさまざまな操作を行うメソッドをオーバーロードします。例

    class TestClass<T>
{
  public void GetName<string>()
  {
      //do work knowing that the type is a string    
  }

  public string GetName<int>()
  {
      //do work knowing that the type is an int

  } 

  public string GetName<int>(int addNumber)
  {
      //do work knowing that the type is an int (overloaded)    
  } 

  public string GetName<DateTime>()
  {
      //do work knowing that the type is a DateTime

  } 

  public string GetName<customObject>()
  {
      //do work knowing that the type is a customObject type    
  }

}

これでGetNameメソッドを呼び出すことができ、オブジェクトを初期化したときにすでに型を渡したため、正しいメソッドが見つかり実行されます。

TestClass foo = new TestClass<int>();

//executes the second method because that's the only one with a "int" type
foo.GetName();

これは可能ですか、それとも夢を見ているだけですか?

役に立ちましたか?

解決

あなたがしようとしていることは、このように可能です:

class TestClass<T>
{
   public string GetName<T>()
   {
      Type typeOfT = typeof(T);
      if(typeOfT == typeof(string))
      {
          //do string stuff
      }
   }
}

これは 可能ですが、ジェネリックの目的を打ち負かすようなものです。ジェネリックのポイントは、タイプが重要ではない場合であるため、この場合はジェネリックが適切であるとは思わない。

他のヒント

C#では特殊化はできません。 C#で最も近いものは次のとおりです

public void Example() {
  public static void Method<T>(T value) { ... }
  public static void Method(int value){ ... }
  public static void Method(string) { ... }
}

C#コンパイラは、汎用メソッドよりも非汎用メソッドを優先します。これは、intパラメーターで呼び出すと、intオーバーロードと汎用オーバーロードにバインドされることを意味します。

Example.Method(42);  // Method(int)
Example.Method(new Class1())  // Method<T>(T)

メソッドがジェネリックに呼び出されたときに適用されないため、これはあなたを噛みます。その場合、タイプに関係なく汎用オーバーロードにバインドされます。

public void Gotcha<T>(T value) {
  Example.Method(value);
}

Gotcha(42);  // Still calls Example.Method<T>()

<!> quot; Specialization <!> quot; C#ではC ++のようにはできません。 .NETジェネリックでは、<!> lt; T <!> gt;のジェネリッククラスまたはメソッド。 Tのすべての可能な値で同じでなければなりません。これにより、ランタイムは、TestClass <!> lt; string <!> gt;など、2つの異なる参照タイプの最適化を実行できます。およびTestClass <!> lt; List <!> lt; int <!> gt; <!> gt;は、同じマシン言語コードを共有します。 (異なる値型は個別のマシンコードを取得しますが、それでも特殊化することはできません。)

次のような汎用インターフェイスまたは基本クラスを作成すると役立つ場合があります:

abstract class Base<T> {
  public abstract T GetName();
  // any common code goes here that does not require specialization
}

そして派生クラスに特化する:

class IntVersion : Base<int> {
  public override int GetName() { return 1; }
  public int GetName(int addNumber) { ... }
}
class StringVersion : Base<string> {
  public override string GetName() { return "foo"; }
}
class DateTimeVersion : Base<DateTime> {
  public override DateTime GetName() { return DateTime.Now; }
}

いいえ、これは不可能です。あなたがやろうとしていることは、C ++でのテンプレートの特殊化に似ています。これはC#では(残念ながら)不可能です。

if / elseまたはスイッチをオンにする必要があります

typeof(T)

特殊な実装を呼び出す。

ただし、Tの型を、クラス(参照値)または構造体(値)、または特定のベースクラスのサブクラスのいずれかに制限できます。

 public Foo<T> DoBar<T>() where T : FooBase;

c#は、このようなディスパッチをサポートしていません。

そして、これは、メソッドのオーバーロードを行う正しい方法ではありません(Error'TestClass 'は、同じパラメーター型を持つ' GetName 'というメンバーを既に定義しています)<!> lt; <!> gt;メソッドシグネチャの一部ではありません。

クラス拡張メソッドを使用すると効果がありますか?

基本的に、必要なクラスにメソッドを追加して、同じ方法で呼び出すことができます。

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int GetName(this String str)
        {
            ...
        }
    }   
}

使用方法:

myString.GetName();

クラスでタイプ固有の作業を行う必要がある場合、クラスはジェネリックではありません。おそらく、処理するタイプごとに個別のクラスを作成する必要があります。汎用的である適切な理由がある機能がある場合、それを共通の基本クラスに入れることができます。

例:

abstract class TestClass<T>
{
    public List<T> Items { get; set; }

    // other generic (i.e. non type-specific) code
}

class IntTestClass : TestClass<int>
{
    public string GetName()
    {
        // do work knowing that the type is an int
    }

    // other code specific to the int case
}

class StringTestClass : TestClass<string>
{
    public string GetName()
    {
        // do work knowing that the type is a string
    }

    // other code specific to the string case
}

BFreeが述べたように、ifツリー、またはより多くの場合switchステートメントでこれを行うことができますが、ある時点でメソッドを記述して.Netにそれを理解させることができます。時間の経過とともに過負荷になります。

.Netのパフォーマンス面ではかなり安価ですが、解決策はリフレクションがあります:

using System.Reflection;
...

public string DoSomething(object val)
{
    // Force the concrete type
    var typeArgs = new Type[] { val.GetType() };

    // Avoid hard-coding the overloaded method name
    string methodName = new Func<string, string>(GetName).Method.Name;

    // Use BindingFlags.NonPublic instead of Public, if protected or private
    var bindingFlags = BindingFlags.Public | BindingFlags.Instance;

    var method = this.GetType().GetMethod(
        methodName, bindingFlags, null, typeArgs, null);

    string s = (string)method.Invoke(this, new object[] { val });

    return s;
}

基本的には、Reflectionフレームワークにswitchステートメントを実行するよう指示しているだけです。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top