Вызовать общий метод с общим типом из system.type
-
27-10-2019 - |
Вопрос
Ниже приведен пример кода и вопрос, обратите внимание, что я не могу использовать C# 4.0 и динамическое ключевое слово.
static class TestClass
{
static void Main(string[] args)
{
Object o = "Previous value";
Test(ref o);
Trace.WriteLine(o);
}
static public void Test<T>(ref T obj)
{
// The goal is to somehow invoke Test2 with the real type of obj, i.e the type in obj.GetType()
// 1st try:
Test2(ref obj); // This doesn't work because the type in Test2 will be the same as T here.
// 2nd try:
MethodInfo mi = typeof(TestClass).GetMethod("Test2");
mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
mi.Invoke(null, new Object[] { obj }); // obj is no longer by reference so we need to store the object array and copy back the result after the call
// 3rd try, successful implementation by the smartest mind of stack overflow :)
}
static public void Test2<T>(ref T s)
{
if (typeof(T) == typeof(String))
{
s = (T)(Object)"Hello world!";
}
}
}
Я также попробовал еще несколько методов, используя Delegate.createdelegate, но без удачи. Это вообще возможно?
РЕДАКТИРОВАТЬ: Я не боюсь использовать динамический метод (и MSIL Assembler), но мои знания в этой области очень ограничены.
EDIT2: Вот пример, который ближе к тому, что я действительно пытаюсь сделать:
public static class TypeHandler<T>
{
public delegate void ProcessDelegate(ref T value);
public static readonly ProcessDelegate Process = Init();
private static ProcessDelegate Init()
{
// Do lot's of magic stuff and returns a suitable delegate depending on the type
return null;
}
}
static class TestClass
{
static public void Main(string[] args)
{
Object o = "Previous value";
Test(ref o);
Trace.WriteLine(o);
}
static public void Test<T>(ref T obj)
{
if (obj is T)
{
// Optimized, common case
TypeHandler<T>.Process(ref obj);
return;
}
Type t = obj.GetType();
// How to call the delegate found in TypeHandler<t>.Process ? (I can get delegate but I can't call it).
}
}
Решение
Обновление 3: Хорошо, так как вы в порядке с уродливым решением, вы можете проверить Недокументированный __refvalue
а также __makeref
ключевые слова.
Кажется, ваша проблема в том, что вы хотите иметь возможность указать тип для ref object
параметр быть преобразованный или же измененный к.
Проблема в том, что вы не можете просто произвольно присвоить переменную любого типа T
в string
, Например. Так что вам нужно пройти в ref object
или же ref string
Для этого вообще.
Мне кажется oberfreak прибил то, чего вы пытались достичь (я изначально не заметил, что вы инициализировались o
как string
и таким образом явно хотел его действительный тип, чтобы влиять на поведение Test2
функция). Его ответ имеет правильный подход для вас.
Обновлять: Вы упоминаете в комментарии, что то, что вы пытаетесь сделать, - это динамическое поведение, которое может быть достигнуто с помощью словаря. Я предполагаю, что это выглядит примерно так?
Обновление 2: Обновлено это Пример на основе ваш Обновленный пример.
public static class TypeHandler // note: get rid of generic T parameter
{
delegate void ProcessDelegate(ref object obj); // again, not generic
static Dictionary<Type, ProcessDelegate> processors = new Dictionary<Type, ProcessDelegate>()
{
{ typeof(string), (ref object obj) => { obj = "Hello, world!"; } }
// etc.
};
public static void Process(ref object obj)
{
processors[obj.GetType()].Invoke(ref obj);
}
}
Что должно сработать. Но вы не можете добиться того же с дженериками, потому что этого невозможно сделать (как вы знаете):
// not allowed
// |
// -----------
// | |
TypeHandler<o.GetType()>.Process(ref o);
Если там мы, тогда вы будете готовы. Но единственный возможный способ сделать это - это использование отражения, что является уродливым и дорогим для чего -то подобного и явно победит ваше намерение сохранить это простое и обеспечить хорошую производительность.
Другие советы
Ваш комментарий выглядит так, как будто вы уже понимаете, как это сделать:
MethodInfo mi = typeof(TestClass).GetMethod("Test2");
mi = mi.MakeGenericMethod(new Type[] { obj.GetType() });
object[] args = new object[] { obj };
mi.Invoke(null, args);
obj = (T) args[0];
Это действительно просто превращает ваш комментарий в код. Это как -то не делает то, что вы хотите?
Основные вопросы, на мой взгляд, в том, что вы хотите сделать?
Если вы просто хотите назначить строку на справочный объект, вы можете попробовать это:
Дженерики могут быть определены во время выполнения, но это не удобно и должно быть сделано по отражению ... в моем оптине это «нет», но просто оптинион
Попробуйте использовать object.GetType()
Чтобы получить текущий тип объекта.
static class TestClass {
static void Main(string[] args) {
Object o = "Previous value";
Test(ref o);
Console.WriteLine(o);
Console.ReadLine();
}
static public void Test<T>(ref T obj) {
Object o = (Object)obj;
Test2(ref o);
obj = (T)o;
}
static public void Test2(ref object s) {
if (s.GetType().Equals(typeof(String))) {
s = "Hello world!";
}
}
}
Правильным способом реализации метода тестирования2 будет
static public void Test2<T>(ref T s)
{
if (s is string)
{
s = (T)(Object)"Hello world!";
}
}
Или я что -то здесь упускаю?
Если вы можете изменить ref
до регулярного возвращения, вы можете массово обманывать 4.0 через dynamic
:
dynamic foo = obj;
Test(foo);
это сейчас:
Test<TheActualTypeOfObj>(obj);
Полный пример:
static void Main(string[] args)
{
object o = "Previous value";
o = Test2((dynamic)o);
Trace.WriteLine(o);
}
static public T Test2<T>(T s)
{
if (typeof(T) == typeof(string))
{
s = (T)(object)"Hello world!";
}
return s;
}
который пишет "Привет, мир!"