Вызов Invoke MethodBase для конструктора (отражение)

Прежде всего, извините, если об этом уже спрашивали. Я провел довольно сложный поиск и ничего подобного не нашел, но, возможно, что-то упустил.

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

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

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

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

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

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


РЕДАКТИРОВАТЬ

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 с помощью MethodBase"s Invoke метод, поэтому я не хотел добавлять ненужную информацию и слишком много кода для вас, ребята, чтобы прочитать. Тем не менее, мне нравится ваше использование Func<,> Гораздо лучше, так что я перехожу на это. Также делает Clone Метод generic является хорошим дополнением, но в моем случае вызывающая сторона не знает тип объекта, поэтому я оставлю его не универсальным.

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

1 ответ

Решение

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


Обновление: у вас уже есть каскадный поиск для 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;
    }
}
Другие вопросы по тегам