Как обернуть статический класс в объект нестатического экземпляра (динамически) - PullRequest
12 голосов
/ 31 мая 2011

У меня интересная проблема. Мне нужно динамически обернуть статические классы. То есть вернуть не статический экземпляр моим вызывающим. e.g.:

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, см .: мой проект . Поэтому я хотел бы иметь возможность (в JavaScript):

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

Спасибо всем.

Ответы [ 4 ]

2 голосов
/ 31 мая 2011

Попробуйте создать класс-оболочку, который наследуется от 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;
        }
    }
}

Надеюсь, это сработает.

1 голос
/ 02 июня 2011

Я бы сказал, пойти на поколение ИЛ. Создание прокси - довольно простой сценарий. Я написал об этом в блоге: einarwh.posterous.com/patching-polymorphic-pain-at-runtime. Сценарий другой, но решение практически идентичное.

Вы можете сделать то же самое, что и в сообщении в блоге, за исключением того, что вам не нужно загружать ссылку 'this' в стек (поскольку вы выполняете статические вызовы методов).

1 голос
/ 01 июня 2011

Хорошо, хорошо, решение, которое я придумала, заключается в следующем и было найдено, прочитав и изучив сообщение Эйнара в блоге , которое он разместил в комментарии выше.Спасибо, Эйнар.

Но я решил опубликовать здесь свое решение с полным кодом на тот случай, если оно может кому-нибудь помочь в будущем:

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);
    }
  }
}
1 голос
/ 31 мая 2011

Итак, скажем, что мы поиграемся с методом «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;
...

Это даст вам делегат, который будет принимать массив типов(параметры универсального типа) и создайте из него рабочий делегат.

...