12

興味深い問題があります。静的クラスを動的にラップする必要があります。つまり、非静的インスタンスを呼び出し元に返します。例えば:

public object CreateInstance(string className) {
  Type t = assembly.GetType(className);
  if (IsStatic(t)) {
    return CreateStaticWrapper(t);
  } else {
    return Activator.CreateInstance(t);
  }
}

だから私が必要とするのは、実装方法に関するポインタCreateStaticWrapperです。

注: 残念ながら動的オブジェクトは使用できません。

それで、私のオプションは何ですか?私はIL生成を学ぶことに熱心ではありませんか?IL生成(Reflection.Emit、または他の方法はありますか?)が進むべき道である場合、そこにポインタがありますか?

編集: デリゲートの辞書を返すことができることに注意することが重要です。だから私はこれを使うことができDelegate.CreateDelegateましたが、オーバーロードされたメソッドとジェネリックメソッドを処理する方法を理解できないようです.

Edit2: 別のオプションは、Emit を使用して型に空のコンストラクターを挿入することです。再びポインターはありますか? これは静的とマークされた型でも可能ですか? static キーワードは IL に入りますか?

Edit3: 少しのコンテキストについては、これを javascript 環境に渡します: my projectを参照してください。だから私は(JavaScriptで)できるようにしたいと思います:

var fileHelper = .create('System.IO.File');
if (fileHelper.Exists(fileName)) { fileHelper.Delete(fileName); }

皆さんありがとう。

4

4 に答える 4

2

から継承するラッパー クラスを作成してみてくださいSystem.Dynamic.DynamicObject。ラッパー クラスでは、リフレクションを使用して静的クラスのメソッドを呼び出します。

次のようなものが必要です。

public class StaticWrapper<T> : System.Dynamic.DynamicObject
{
    private static readonly Type t = typeof(T);
    public static int MyProperty { get; set; }
    public override bool TryInvokeMember(System.Dynamic.InvokeMemberBinder binder, object[] args, out object result)
    {
        try
        {
            result = t.InvokeMember(binder.Name, BindingFlags.Static | BindingFlags.Public, null, null, args);
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
    public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
    {
        try
        {
            var p = t.GetProperty(binder.Name);
            if (p != null)
                result = p.GetValue(null, null);
            else
            {
                var f = t.GetField(binder.Name);
                if (f != null) result = f.GetValue(null);
                else { result = null; return false; }
            }
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }
    public override bool TrySetMember(System.Dynamic.SetMemberBinder binder, object value)
    {
        try
        {
            var p = t.GetProperty(binder.Name);
            if (p != null)
                p.SetValue(null, value, null);
            else
            {
                var f = t.GetField(binder.Name);
                if (f != null) f.SetValue(null, value);
                else return false;
            }
            return true;
        }
        catch (SystemException)
        {
            return false;
        }
    }
}

それがうまくいくことを願っています。

于 2011-05-31T10:51:02.513 に答える
1

さて、私が思いついた解決策は次のとおりであり、上記のコメントとして投稿したEinar のブログ投稿を読んで調べていることがわかりました。ありがとうアイナー。

しかし、将来誰かに役立つ可能性がある場合に備えて、完全なコードソリューションをここに投稿すると思いました:

using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;

namespace js.net.jish.Command
{
  public class StaticTypeWrapper
  {
    private readonly Type staticType;

    public StaticTypeWrapper(Type staticType)
    {
      this.staticType = staticType;
    }

    public object CreateWrapper()
    {
      string ns = staticType.Assembly.FullName;      
      ModuleBuilder moduleBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName(ns), AssemblyBuilderAccess.Run).DefineDynamicModule(ns); 
      TypeBuilder wrapperBuilder = moduleBuilder.DefineType(staticType.FullName, TypeAttributes.Public, null, new Type[0]);  
      foreach (MethodInfo method in staticType.GetMethods().Where(mi => !mi.Name.Equals("GetType")))
      {
        CreateProxyMethod(wrapperBuilder, method);
      }
      Type wrapperType = wrapperBuilder.CreateType();
      object instance = Activator.CreateInstance(wrapperType);
      return instance;
    }

    private void CreateProxyMethod(TypeBuilder wrapperBuilder, MethodInfo method)
    {
      var parameters = method.GetParameters();

      var methodBuilder = wrapperBuilder.DefineMethod(method.Name, MethodAttributes.Public | MethodAttributes.Virtual, method.ReturnType, parameters.Select(p => p.ParameterType).ToArray());
      var gen = methodBuilder.GetILGenerator();

      for (int i = 1; i < parameters.Length + 1; i++)
      {
        gen.Emit(OpCodes.Ldarg, i); 
      }
      gen.Emit(OpCodes.Call, method); 
      gen.Emit(OpCodes.Ret);
    }
  }
}
于 2011-06-01T07:38:47.923 に答える
1

IL生成に行くと思います。プロキシの作成は非常に単純なシナリオです。私は実際にそれについてブログ投稿を書きました: einarwh.posterous.com/patching-polymorphic-pain-at-runtime。シナリオは異なりますが、ソリューションはほぼ同じです。

「this」参照をスタックにロードする必要がないことを除いて、基本的にブログ投稿とまったく同じように行うことができます (静的メソッド呼び出しを行っているため)。

于 2011-06-02T15:39:14.733 に答える
1

それで、「Delegate.CreateDelegate」の方法をいじってみましょう。その後、他の問題について詳細を確認できるかどうか見てみましょう.

public static object Generate(Type t)
{
    if(IsStatic(t))
    {
        var dictionary = new Dictionary<string, Delegate>();
        foreach (var methodInfo in t.GetMethods())
        {
            var d = Delegate.CreateDelegate(t, methodInfo);
            dictionary[methodInfo.Name] = d;
        }
        return dictionary;
    }
    return Activator.CreateInstance(t);
}

静的クラスは「封印」されているため、継承できません。したがって、「過負荷」の意味がわかりません。methodInfo.MakeGenericMethod(...)ジェネリック メソッドの場合、辞書に追加する前にを呼び出す必要があります。しかし、その場合、事前にタイプを知る必要がありますが、そうではないと思います...代わりに、次のようなこともできます:

...
if (methodInfo.IsGenericMethod)
{
    d = new Func<MethodInfo, Type[], Delegate>(
        (method, types) =>
        Delegate.CreateDelegate(
            method.DeclaringType, method.MakeGenericMethod(types)));
}
dictionary[methodInfo.Name] = d;
...

これにより、型配列 (ジェネリック型パラメーター) を受け取るデリゲートが得られ、そこから作業デリゲートが生成されます。

于 2011-05-31T18:03:42.460 に答える