2010-03-30 4 views
3

Прежде всего, извините, если это было задано раньше. Я сделал довольно полный поиск и не нашел ничего подобного, но я, возможно, что-то пропустил.Вызов методаBase's Invoke на конструкторе (отражение)

А теперь на вопрос: Я пытаюсь вызвать конструктора через отражение, без везения. В принципе, у меня есть объект, который я хочу клонировать, поэтому я ищу конструктор копирования для его типа, а затем хочу вызвать его. Вот что у меня есть:

public Object clone(Object toClone) { 
    MethodBase copyConstructor = type.GetConstructor(
     new Type[] { toClone.GetType() }); 
    return method.Invoke(toClone, new object[] { toClone }); //<-- doesn't work 
} 

Я называю вышеупомянутый метод следующим образом:

List<int> list = new List<int>(new int[] { 0, 1, 2 }); 
List<int> clone = (List<int>) clone(list); 

Теперь обратите внимание на метод Invoke я использую MethodBase «s Invoke. ConstructorInfo предоставляет метод Invoke, который делает работу, если вызывается так:

return ((ConstructorInfo) method).Invoke(new object[] { toClone }); 

Однако я хочу использовать метод MethodBase «s, потому что в действительности вместо того, чтобы искать конструктор копирования каждый раз, когда я буду хранить его в словаре , а словарь содержит как методы, так и конструкторы, поэтому это Dictionary<MethodBase>, а не Dictionary<ConstructorInfo>. Я мог бы, конечно, бросить на ConstructorInfo, как я выше, но я бы предпочел избежать литья и использовать метод MethodBase напрямую. Я просто не могу понять правильные параметры.

Любая помощь? Спасибо.


EDIT

Benjamin,
спасибо за ваши предложения. Я был на самом деле делает именно то, что вы предлагаете в вашем втором редактировать, за исключением (и это большой «кроме») мой словарь был где

class ClonerMethod { 

    public MethodBase method; 
    public bool isConstructor; 

    ... 

    public Object invoke(Object toClone) { 
     return isConstructor ? 
      ((ConstructorInfo) method).Invoke(new object[] { toClone }) : //<-- I wanted to avoid this cast 
      method.Invoke(toClone, null); 
    } 

} 

И тогда я назвал ClonerMethod «s invoke на то, что я нашел в словаре. Я не добавлял код ко всем сделкам, потому что ответ, который я искал, - это просто вызов Invoke на ConstructorInfo с использованием метода MethodBaseMethodBase, поэтому я не хотел добавлять лишнюю информацию и слишком много кода для вы, ребята, читаете. Однако мне очень нравится использование Func<,>, поэтому я перехожу к этому. Кроме того, создание общего метода Clone является хорошим дополнением, но в моем случае вызывающий не знает тип объекта, поэтому вместо этого я сохраню его не общее.

Я не знал о Func<,>, и, если бы знал об операторе лямбды, я забыл (мне раньше не нужно было что-то подобное), поэтому я действительно многому научился от вашего ответа. Я всегда люблю изучать новые вещи, и это пригодится в будущем, поэтому большое спасибо! :)

+0

Вы должны использовать метод, чтобы вызвать правильный метод. Это никогда не должно быть проблемой, вы будете обрабатывать возвращаемое значение по-разному, когда вы вызываете конструктор. –

+0

@nobugz: см. Обновления OP ниже моего ответа. Кажется, идея (и проблема с реализацией указанной идеи) заключается в том, чтобы хранить способы клонирования объекта, которые могут быть методами ('ICloneable.Clone') или конструкторы (конструктор копирования, выше). Вызывающий код не должен заботиться, если я получу его сейчас, и OP trie [sd], чтобы найти способ одинаково обрабатывать оба способа, как 'MethodBase.Invoke()'. –

+1

Хорошо, тогда оператор * is * будет работать нормально, чтобы обнаружить, что это фактически ConstructorInfo. –

ответ

4

Если вы знаете, что объект имеет такой конструктор, подумал ли вы об использовании этой перегрузки Activator.CreateInstance?


Update: Так что у вас есть поиск каскадный для MethodInfo/MethodBase уже и хранить их -> Вы не хотите/не можете использовать Activator.

В этом случае я не вижу способ делать то, что вы хотите, без приведения.Но, возможно, вы могли бы изменить архитектуру для хранения Dictionary<Type, Func<object, object>> и вместо этого добавить эти Func<> экземпляров. Делает вызывающий код лучше (я предполагаю), и позволит вам сделать этот бросок сразу:

// Constructor 
dictionary.Add(type, 
    source => ((ConstructorInfo) method).Invoke(new object[] {source}) 
); 

// Clone 
dictionary.Add(type, 
    source => method.Invoke(source, new object[]{}) 
); 

В самом деле, так как вы заботитесь только о разнице между конструктором и обычным способом в том самом месте, где вы берете их, вам не понадобится бросок, не так ли?

// Constructor 2 
dictionary.Add(type, 
    source => yourConstructorInfo.Invoke(new object[] {source}) 
); 

Если не я что-то не хватает (вполне возможно, конечно) это может решить эту проблему, делая это один раз на определяющем сторону забора, и вызывающий абонент не должен был бы возражать, если это конструктор или не?

В последний раз, когда я собираюсь остановить редактируемый спам. Мне было скучно и придумал следующий код. Это то, чего вы пытаетесь достичь?

public class Cloner { 
    private readonly IDictionary<Type, Func<object, object>> _cloneMap = 
      new Dictionary<Type, Func<object, object>>(); 

    public T Clone<T>(T source) { 
     Type sourceType = source.GetType(); 
     Func<object, object> cloneFunc; 

     if (_cloneMap.TryGetValue(sourceType, out cloneFunc)) { 
      return (T)cloneFunc(source); 
     } 

     if (TryGetCopyConstructorCloneFunc(sourceType, out cloneFunc)) { 
      _cloneMap.Add(sourceType, cloneFunc); 
      return (T)cloneFunc(source); 
     } 

     if (TryGetICloneableCloneFunc(sourceType, out cloneFunc)) { 
      _cloneMap.Add(sourceType, cloneFunc); 
      return (T)cloneFunc(source); 
     } 

     return default(T); 
    } 

    private bool TryGetCopyConstructorCloneFunc(Type type, 
        out Func<object, object> cloneFunc) { 
     var constructor = type.GetConstructor(new[] { type }); 
     if (constructor == null) { 
      cloneFunc = source => null; 
      return false; 
     } 
     cloneFunc = source => constructor.Invoke(new[] { source }); 
     return true; 
    } 

    private bool TryGetICloneableCloneFunc(Type type, 
        out Func<object, object> cloneFunc) { 
     bool isICloneable = typeof(ICloneable).IsAssignableFrom(type); 
     var cloneMethod = type.GetMethod("Clone", new Type[] { }); 
     if (!isICloneable || (cloneMethod == null)) { 
      cloneFunc = source => null; 
      return false; 
     } 
     cloneFunc = source => cloneMethod.Invoke(source, new object[] {}); 
     return true; 
    } 
} 
+0

На самом деле, это сложнее, чем это. Фактический метод clone: ​​ 1. Поиск словаря, о котором я упоминал ранее, чтобы увидеть, содержит ли он уже метод, который будет клонировать объект, чтобы избежать необходимости искать его с отражением на каждом вызове клонирования. 2. Если словарь не содержит такого метода, я просматриваю конструктор копирования, как показано выше. Если я найду его, я добавлю его в словарь и вызову его. 3. Если я не смог найти конструктор копирования, я вижу, является ли объект ICloneable, и если это так, я получаю его метод Clone, добавьте его в словарь и вызовите его. – Alix

+0

... Так что я вообще не хочу вызывать Activator.CreateInstance во всех случаях. [ПРИМЕЧАНИЕ: мне пришлось поместить это в другой комментарий из-за нехватки места] – Alix

+0

Лучше обновите вопрос с более подробной информацией в следующий раз и, при желании, дайте плакат (мне) хедз-ап «Смотрите обновленное сообщение». Больше опций форматирования и без ограничений символов .. :) –