Подтвердить что ты не робот

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

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

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); }

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

4b9b3361

Ответ 1

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

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

Ответ 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;
        }
    }
}

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

Ответ 3

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

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

Ответ 4

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

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

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);
    }
  }
}