2016-03-08 6 views
6

Type(文字列として)とコンストラクタ引数の配列を指定してオブジェクトインスタンスを作成する必要がある状況です。文字列でクラスのインスタンスを作成しますが、暗黙的な変換を考慮します - C#Reflection

これは私がそれを達成する方法である:

public object Create(string Name, params object[] Args) 
{ 
    return Activator.CreateInstance(Type.GetType(Name), Args); 
} 

これは、ほとんどのケースでは正常に動作しますが、それに問題があります。暗黙的な変換は考慮されません。


我々は

public class ImplicitTest 
{ 
    public double Val { get; set; } 

    public ImplicitTest(double Val) 
    { 
     this.Val = Val; 
    } 

    public static implicit operator int(ImplicitTest d) 
    { 
     return (int)d.Val; 
    } 
} 

を定義しintへの暗黙的な変換と単純なクラスを持っているし、我々はそれのコンストラクタのパラメータ

としてint型を使用するクラスを持っていると言う、私は私が何を意味するか説明しましょう
public class TestClass 
{ 
    public int Val { get; set; }   

    public TestClass(int Val) 
    { 
     this.Val = Val; 
    } 
} 

ここで、TestClassのインスタンスを作成したいとします。 new TestClass(5)です。この場合、コンストラクタが指定する(int)正確なパラメータ型を使用します。ただし、ImplicitTestクラスを使用してクラスのインスタンスを作成することもできます(new TestClass(new ImplicitTest(5.1)))。これは、パラメータがImplicitTestからintに暗黙的に変換されるためです。 Activator.CreateInstance()はこれを行いません。


我々のようなTestClassをのインスタンスを作成する前から私たちのCreate(string Name, params object[] Args)メソッドを使用することができます:Create("ThisNamespace.TestClass", 5)、これは動作します。私が経験しています 問題は、したがって、このスニペットは、エラーをスローし、暗黙的な変換を使用しようとすると、動作しないということである。Create("ThisNamespace.TestClass", new ImplicitTest(5.1))

私は、このことを考慮してどのように絶対にないアイデアを持っていないが、それは私のユースケースのために重要です。おそらく、Activator.CreateInstance()関数のパラメータがいくつかありますか?私は行方不明ですか?あるいは私の目標を達成するために使用できる全く異なるメソッドがありますか?私は答えを見つけることができませんでした。


TL; DR

//Valid 
new TestClass(5); 

//Valid 
new TestClass(new ImplicitTest(5.1)); 

//Valid 
Activator.CreateInstance(Type.GetType("ThisNamespace.TestClass"), 5); 

//Invalid, throws System.MissingMethodException 
Activator.CreateInstance(Type.GetType("ThisNamespace.TestClass"), new ImplicitTest(5.1)); 

なぜ?

+0

似たようなスレッド:http://stackoverflow.com/questions/20932208/activator-createinstance-not-working-for-implicit-cast-scenario –

+1

「暗黙の演算子int」は「静的」なのでしたがって、 'object'クラスのメソッドをオーバーライドしません。そして、 'Activator.CreateInstance'を渡すと、あなたが渡す' Args'は 'object'sです。 –

+0

.NETの動的インフラストラクチャを活用することは可能です。 'Microsoft.CSharp'アセンブリには、オーバーロード選択のためのC#"規則 "があります。 https://ideone.com/UkGm4Eを参照してください。私は "ジェネリック"作業メソッド(ダイナミックはC#の私のフィールドではありません)に変換するのに十分なエキスパートではありません – xanatos

答えて

2

暗黙の型変換は、CLRではなくC#コンパイラの機能です。

したがって、Reflectionを使用する場合は、手動で達成する必要があります。

+0

これは 'Binder'クラスのタスク(' ConstructorInfo GetConstructor(BindingFlags bindingAttr、バインダーバインダー、... ')で使用されていることを追加します)悲しいことに、' CSharpBinder'はインターネット上にはありません – xanatos

0

クラスにIConvertibleを実装しますか?
必要なものだけをすべて実装する必要はありません。
「はい」の場合は、次のようなものを使用できます。 (あなたは私が今のように、特定のコンストラクタを検索する必要はありませんので、あなたはそれをより汎用的にすることができます)

using System; 
using System.Reflection; 

namespace ConsoleApplication 
{ 
    class Program 
    { 
     static void Main() 
     { 
      var theType = Type.GetType("ConsoleApplication.TestClass"); 

      ConstructorInfo ctor = theType.GetConstructors()[0]; 
      var argumentType = ctor.GetParameters()[0].ParameterType; 

      object contructorArgument = new ImplicitTest(5.1); 

      object instance = ctor.Invoke(new object[] { Convert.ChangeType(contructorArgument, argumentType) }); 

      Console.ReadLine(); 
     } 
    } 

    public class TestClass 
    { 
     public int Val { get; set; } 

     public TestClass(int Val) 
     { 
      this.Val = Val; 
     } 

    } 

    public class ImplicitTest : IConvertible 
    { 
     public double Val { get; set; } 

     public ImplicitTest(double Val) 
     { 
      this.Val = Val; 
     } 

     public static implicit operator int(ImplicitTest d) 
     { 
      return (int)d.Val; 
     } 

     public int ToInt32(IFormatProvider provider) 
     { 
      return (int)this; 
     } 

     //Other methods of IConvertible 
    } 
} 
0

私はC#のの動的部分の専門家ではないんだけど、これは正しく動作しているようです:

using System; 
using System.Linq; 
using System.Runtime.CompilerServices; 
using Microsoft.CSharp.RuntimeBinder; // Requires reference to Microsoft.CSharp 

public class ImplicitTest 
{ 
    public double Val { get; set; } 

    public ImplicitTest(double val) 
    { 
     this.Val = val; 
    } 

    public static implicit operator int(ImplicitTest d) 
    { 
     return (int)d.Val; 
    } 
} 

public class TestClass 
{ 
    public int Val { get; set; } 

    public TestClass(int val = 5) 
    { 
     this.Val = val; 
    } 

    public TestClass(int val1, int val2) 
    { 
     this.Val = val1 + val2; 
    } 

    public TestClass(int val1, int val2, int val3, int val4, int val5, int val6, int val7, int val8, int val9, int val10, int val11, int val12, int val13, int val14) 
    { 
     this.Val = val1 + val2 + val3 + val4 + val5 + val6 + val7 + val8 + val9 + val10 + val11 + val12 + val13 + val14; 
    } 
} 

public static class DynamicFactory 
{ 
    private static readonly CallSiteBinder callsiteBinder0 = Binder.InvokeConstructor(
     CSharpBinderFlags.None, 
     typeof(DynamicFactory), 
     // It is OK to have too many arguments :-) 
     new CSharpArgumentInfo[] 
     { 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), // 0 parameters 
     }); 

    private static readonly CallSiteBinder callsiteBinder = Binder.InvokeConstructor(
     CSharpBinderFlags.None, 
     typeof(DynamicFactory), 
     // It is OK to have too many arguments :-) 
     // Note that this "feature" doesn't work correctly with Mono in the 
     // case of 0 arguments 
     new CSharpArgumentInfo[] 
     { 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.IsStaticType | CSharpArgumentInfoFlags.UseCompileTimeType, null), // 0 parameters 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), // 1 parameter 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), // 2 parameters 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
      CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), // 14 parameters 
     }); 

    // Quirk of Mono with 0 arguments. See callsiteBinder0 
    private static readonly CallSite<Func<CallSite, Type, object>> CallSite0 = CallSite<Func<CallSite, Type, object>>.Create(callsiteBinder0); 

    private static readonly CallSite<Func<CallSite, Type, object, object>> CallSite1 = CallSite<Func<CallSite, Type, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object>> CallSite2 = CallSite<Func<CallSite, Type, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object>> CallSite3 = CallSite<Func<CallSite, Type, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object>> CallSite4 = CallSite<Func<CallSite, Type, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object>> CallSite5 = CallSite<Func<CallSite, Type, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object>> CallSite6 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object>> CallSite7 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object>> CallSite8 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object>> CallSite9 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object>> CallSite10 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object>> CallSite11 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object>> CallSite12 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object, object>> CallSite13 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 
    private static readonly CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>> CallSite14 = CallSite<Func<CallSite, Type, object, object, object, object, object, object, object, object, object, object, object, object, object, object, object>>.Create(callsiteBinder); 

    public static object Create(string typeName, params object[] args) 
    { 
     return Create(Type.GetType(typeName), args); 
    } 

    public static object Create(Type type, params object[] args) 
    { 
     if (type == null) 
     { 
      throw new ArgumentNullException("type"); 
     } 

     if (args == null) 
     { 
      args = new object[0]; 
     } 

     object obj; 

     switch (args.Length) 
     { 
      case 0: 
       // Quirk of Mono with 0 arguments. See callsiteBinder0 
       obj = CallSite0.Target(CallSite0, type); 
       break; 

      case 1: 
       obj = CallSite1.Target(CallSite1, type, args[0]); 
       break; 

      case 2: 
       obj = CallSite2.Target(CallSite2, type, args[0], args[1]); 
       break; 

      case 3: 
       obj = CallSite3.Target(CallSite3, type, args[0], args[1], args[2]); 
       break; 

      case 4: 
       obj = CallSite4.Target(CallSite4, type, args[0], args[1], args[2], args[3]); 
       break; 

      case 5: 
       obj = CallSite5.Target(CallSite5, type, args[0], args[1], args[2], args[3], args[4]); 
       break; 

      case 6: 
       obj = CallSite6.Target(CallSite6, type, args[0], args[1], args[2], args[3], args[4], args[5]); 
       break; 

      case 7: 
       obj = CallSite7.Target(CallSite7, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6]); 
       break; 

      case 8: 
       obj = CallSite8.Target(CallSite8, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); 
       break; 

      case 9: 
       obj = CallSite9.Target(CallSite9, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); 
       break; 

      case 10: 
       obj = CallSite10.Target(CallSite10, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); 
       break; 

      case 11: 
       obj = CallSite11.Target(CallSite11, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10]); 
       break; 

      case 12: 
       obj = CallSite12.Target(CallSite12, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11]); 
       break; 

      case 13: 
       obj = CallSite13.Target(CallSite13, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12]); 
       break; 

      case 14: 
       obj = CallSite14.Target(CallSite14, type, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13]); 
       break; 

      default: 
       throw new ArgumentException("Too many parameters"); 
     } 

     return obj; 
    } 
} 

public class Program 
{ 
    public static void Main() 
    { 
     try 
     { 
      Type monoRuntime = Type.GetType("Mono.Runtime"); 

      if (monoRuntime != null) 
      { 
       System.Reflection.MethodInfo displayName = monoRuntime.GetMethod("GetDisplayName", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 

       if (displayName != null) 
       { 
        Console.WriteLine("Mono version {0}", displayName.Invoke(null, null)); 
       } 
      } 

      TestClass tc0 = (TestClass)DynamicFactory.Create("TestClass"); 
      TestClass tc1 = (TestClass)DynamicFactory.Create("TestClass", new ImplicitTest(1.0)); 
      TestClass tc1b = (TestClass)DynamicFactory.Create("TestClass", 1); 
      TestClass tc2 = (TestClass)DynamicFactory.Create("TestClass", new ImplicitTest(1.0), new ImplicitTest(2.0)); 
      TestClass tc14 = (TestClass)DynamicFactory.Create("TestClass", Enumerable.Range(0, 14).Select(x => new ImplicitTest((double)x)).ToArray()); 

      Console.WriteLine(tc0.Val); 
      Console.WriteLine(tc1.Val); 
      Console.WriteLine(tc1b.Val); 
      Console.WriteLine(tc2.Val); 
      Console.WriteLine(tc14.Val); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 
} 

これは、.NETの「動的」部分を活用しています。それはMicrosoft.CSharp.RuntimeBinderを使用してC#のバインディングルールを "認識"しています。他の誰かが指摘したように、ユーザー定義の暗黙的キャストと明示的キャストは.NETのものではなくC#のものなので、バインダーが必要です。完璧なソリューションは、C#のルールを "知っている" System.Reflection.Binderクラスのサブクラスを持つことです。その後、それをType.GetConstructorに渡し、幸せに生きることができます。残念ながらそのような実装はありません。

悲しいことに、デリゲートはCallSiteCallSite<T>の汎用サブクラスに格納され、Tはデリゲートタイプです。このため、大きいswitch命令。

コードのアイデア:https://ideone.com/NoQ67H。 Monoにはquirkがあるので、0のコンストラクタには特別な処理があります。あなたはここで、式フレームワークを利用することができ

0

は「トリック」を行う必要がある例です。

class Program 
{ 
    static object Create(string Name, params object[] Args) 
    { 
     Type theType = Type.GetType(Name); 
     object toReturn = null; 
     // Code wrote quickly, I just try to call all the type constructors... 
     foreach (var constructor in Type.GetType(Name).GetConstructors()) 
     { 
      try 
      { 
       string paramPrefix = "p"; 
       int pIdx = 0; 
       var expParamsConsts = new List<Expression>(); 
       var ctrParams = constructor.GetParameters(); 
       for (int i = 0; i < constructor.GetParameters().Length; i++) 
       { 
        var param = ctrParams[i]; 
        var tmpParam = Expression.Variable(param.ParameterType, paramPrefix + pIdx++); 
        var expConst = Expression.Convert(Expression.Constant(Args[i]), param.ParameterType); 
        expParamsConsts.Add(expConst); 
       } 
       // new Type(...); 
       var expConstructor = Expression.New(constructor, expParamsConsts); 
       // return new Type(...); 
       var expLblRetTarget = Expression.Label(theType); 
       var expReturn = Expression.Return(expLblRetTarget, expConstructor, theType); 
       var expLblRet = Expression.Label(expLblRetTarget, Expression.Default(theType)); 
       // { return new Type(...); } 
       var expBlock = Expression.Block(expReturn, expLblRet); 
       // Build the expression and run it 
       var expFunc = Expression.Lambda<Func<dynamic>>(expBlock); 
       toReturn = expFunc.Compile().Invoke(); 
      } 
      catch (Exception ex) 
      { 
       ex.ToString(); 
      } 
     } 
     return toReturn; 
    } 

    static void Main(string[] args) 
    { 
     var tmpTestClass = Create(TYPE_NAME, 5); 
     tmpTestClass = Create(TYPE_NAME, new ImplicitTest(5.1)); 
    } 
} 

public class ImplicitTest 
{ 
    public double Val { get; set; } 

    public ImplicitTest(double Val) 
    { 
     this.Val = Val; 
    } 

    public static implicit operator int(ImplicitTest d) 
    { 
     return (int)d.Val; 
    } 
} 

public class TestClass 
{ 
    public int Val { get; set; } 

    public TestClass(int Val) 
    { 
     this.Val = Val; 
    } 
} 

はおそらくそうする迅速な方法がありますが、私は式には非常に専門家ではありません...

+0

うん、非常に頻繁に 'Expression'を使用していないのは簡単です。ほとんどの人はローカルのすべての中間体を格納するのではなく、式のツリーを構築するだけです:D関数型プログラミングの影響をあまり受けていないことは明らかです。これはコンパイラが通常処理するもの(例えば、ボクシング、暗黙的な変換、オーバーロードの解決など)を悪用しながら、リフレクションが必要な時はいつでも最適なソリューションです。 – Luaan

+0

この場合、中間変数を使用することにしました。なぜなら、より読みやすいように思えたからです。 –

+1

ええ、それは "関数型プログラミングにはあまり使われていません"ということになります。 FPを少し試してみてください。そうすればもっと快適になります。 C#が時間の経過とともにますます機能的になっていくとき(デリゲート、エクスプレッションツリー、LINQなど)は重要です。要点は、純粋な関数を使用すると、個々の関数呼び出しを独立して考えることができるため、中間体をスキップしても必ずしも読みやすさが低下するとは限りません。しかし実際には、これは単なる文体的なものであり、あまり重要ではありません。しかし、FPはあなたのツールセットのための素晴らしいツールです:) – Luaan