web-dev-qa-db-ja.com

メソッドでc#の任意のデータ型を取得する

私は多くのユニットテストを行っており、ほとんど同じ動作をテストしています。ただし、データ型は変更されます。

任意のデータ型を取ることができるジェネリックメソッドを作成しようとしています。入力パラメーターをvarにしようとしましたが、これは許可されていません。また、c#ジェネリックを調べましたが、通常はリストを扱います。

27
socialMatrix

パラメータをobjectにすることができます:

_public void DoSomething(object arg)
{
   //...
_

または、あなたが私が好むことをして、一般的な方法を作ることができます:

_public void DoSomething<T>(T arg)
{
    //...
_

一般的なアプローチには2つの主要な利点があります。それらが役立つ理由の例を示します。

  1. argのタイプを明示的に指定していなくても、アクセスできます。
  2. 許可するタイプに制約を追加できます。

逆に、objectアプローチにはいくつかの重要な欠点があります。

  1. argobjectとして扱っているため、anyオブジェクトで実行できることしか実行できません。
  2. 値の型をobjectパラメータとして渡すと、変数は boxed になり、パフォーマンスに影響します。大したヒットではありませんが、DoSomethingを数千回続けて呼び出すと、感じ始めるかもしれません。

ジェネリックスと型制約

ジェネリックメソッドに型制約を追加すると、特定の型のみを受け入れるようにメソッドを制限できます。なぜそれが役立つのですか?作業している特定のタイプがわからない(または気にしていない)場合でも、そのタイプについて何か知っているので、その情報を使用できます。

次の設定を検討してください。

_public interface IAnimal 
{ 
    void Move(); 
}
public class Duck : IAnimal
{
    public void Move() 
    { 
        Console.WriteLine("Flying"); 
    }
}
public class Fish : IAnimal
{
    public void Move()
    { 
        Console.WriteLine("Swimming"); 
    }
}
public class Ant : IAnimal
{
    public void Move()
    { 
        Console.WriteLine("Walking"); 
    }
}    
_

IAnimalインターフェイスがあるので、IAnimalの実装anyを対象としたジェネリックメソッドを記述できます。

_public class Program
{
    static void DoMove<T>(T animal) where T : IAnimal
    {
        animal.Move();
    }
    public static void Main(string[] args)
    {            
        Duck duck = new Duck(); 
        Fish fish = new Fish();
        Ant ant = new Ant(); 

        DoMove<Duck>(duck);
        DoMove<Fish>(fish);
        DoMove<Ant>(ant);
    }
}
_

実行: http://rextester.com/GOF1761

DoMoveメソッドを記述するとき、そのパラメーターanimalDuckFishAntであるかどうかは関係ありません、またはその他。重要なのは、animal.Move()を呼び出すことだけです。 _where T : IAnimal_制約を使用したので、コンパイラーは知る必要があるすべてを知っています。

  1. 変数animalのタイプはTです。
  2. Tが何であれ、IAnimalを実装します。
  3. IAnimalを実装するものには、Move()メソッドがあります。
  4. したがって、animal.Move()を安全に呼び出すことができます。

(ちなみに、はい、DoMovestatic void DoMove(IAnimal animal)と書くこともできますが、それは別の議論です。)

型推論(およびその影響の一部)

結構ですが、さらに一歩進めましょう。多くの場合、型パラメーターを指定せずにジェネリックメソッドを呼び出すことができます。これは type inference と呼ばれ、いくつかの入力を節約するだけでなく、異なるタイプのオブジェクトに対して同じ操作を行う場合に役立ちます。

_public static void Main(string[] args)
{            
    IAnimal[] animals = new IAnimal[] 
    {
        new Duck(),
        new Fish(),
        new Ant()
    };

    foreach (IAnimal animal in animals)
    {
        DoMove(animal);
    }
}
_

実行してください: http://rextester.com/OVKIA12317

_DoMove<T>_メソッドを1回記述するだけで、より具体的な型を指定しなくても、任意の型のIAnimalで呼び出すことができます。 _DoMove<T>_がTに使用するタイプを推測できるため、適切なバージョンのMoveが毎回呼び出されます。 DoMove(duck)を呼び出すと、.NETは、あなたが本当にDoMove<Duck>(duck)を意味することを理解し、MoveクラスのDuckメソッドを呼び出します。

51
Justin Morgan

パラメータタイプとしてobjectを取り込むことができます。たぶん、ジェネリックを使うほうがいいでしょう。

void MyMethod<T>(T parm) { ... }

このように、パラメーターは実際にはユーザーが渡したタイプになります-objectや値のタイプのようにボックス化されません。

8
Kirk Woll
void MyTestMethod<T>(T t) { }

あなたに一般的なテスト方法を提供しますが、私が役立つことができる方法を想像することはできません。あなたは何をテストする必要がありますか?タイプTにこれらのメソッドがあることをどのようにして知っていますか? Tは、上記のメソッドではanyタイプにすることができます。上記の例でtから呼び出すことができる唯一のメソッドは、objectの一般的なメソッドです。

実際に行う必要があるのは、テストする1つ以上のタイプに対してcommon behaviourを識別し、インターフェースを介してその動作の構文規約を定義することです。次に、ジェネリックテストメソッドを制約して、そのインターフェイスを実装する型のみを受け入れることができます。

interface IMyInterface
{
    void DoSomething();
} 

void MyTestMethod<T>(T t) where T : IMyInterface
{ 
    t.DoSomething();
}
4
public void YourMethod<T>(T parameter)
{
}
3
Aducci

動的キーワードを使用してみてください。これは、すべての異なるタイプがユニットテストで使用されるものと同じメソッドを持っている場合に機能します。それ以外の場合は、ランタイム例外が発生します。

0
dmg