Как обернуть статический класс в объект нестатического экземпляра (динамически)
У меня есть интересная проблема. Мне нужно динамически переносить статические классы. То есть верните нестатический экземпляр моим абонентам. например:.
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); }
Спасибо всем.
Ответы
Ответ 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);
}
}
}