Общие конструкторы и рефлексия - PullRequest
9 голосов
/ 15 мая 2009

Можно ли увидеть, какой конструктор был универсальным?

internal class Foo<T>
{
  public Foo( T value ) {}
  public Foo( string value ) {}
}

var constructors = typeof( Foo<string> ).GetConstructors();

Свойство ContainsGenericParameters возвращает меня для обоих конструкторов false. Есть ли способ узнать, что конструкторы [0] являются общими? Они оба имеют одинаковую подпись, но я бы хотел назвать «настоящую» строку.

EDIT:

Я хочу вызвать данный тип, используя

ilGen.Emit( OpCodes.Newobj, constructorInfo );

так что мне нужно работать со связанной версией. Но я хотел бы вызвать «лучший» конструктор. Это должно быть стандартным поведением. Когда я звоню

new Foo<string>()

вызывается конструктор со строкой-подписью (а не тот, у которого есть общая подпись). То же самое должно произойти с моим кодом.

Ответы [ 4 ]

8 голосов
/ 15 мая 2009

Требуется System.Reflection.ParameterInfo.ParameterType.IsGenericParameter. Вот прохождение модульного теста VS2008, которое иллюстрирует это:

Класс:

public class Foo<T>
{
    public Foo(T val)
    {
        this.Value = val.ToString();
    }
    public Foo(string val)
    {
        this.Value = "--" + val + "--";
    }

    public string Value { get; set; }
}

Метод испытания:

Foo<string> f = new Foo<string>("hello");
Assert.AreEqual("--hello--", f.Value);

Foo<int> g = new Foo<int>(10);
Assert.AreEqual("10", g.Value);

Type t = typeof(Foo<string>);
t = t.GetGenericTypeDefinition();

Assert.AreEqual(2, t.GetConstructors().Length);

System.Reflection.ConstructorInfo c = t.GetConstructors()[0];
System.Reflection.ParameterInfo[] parms = c.GetParameters();
Assert.AreEqual(1, parms.Length);
Assert.IsTrue(parms[0].ParameterType.IsGenericParameter);

c = t.GetConstructors()[1];
parms = c.GetParameters();
Assert.AreEqual(1, parms.Length);
Assert.IsFalse(parms[0].ParameterType.IsGenericParameter);

Примечательной точкой здесь является проверка parms [0] .ParameterType.IsGenericParameter, которая проверяет, является ли параметр универсальным или нет.

Как только вы нашли свой конструктор, у вас есть ConstructorInfo для передачи Emit.

public System.Reflection.ConstructorInfo FindStringConstructor(Type t)
{
    Type t2 = t.GetGenericTypeDefinition();

    System.Reflection.ConstructorInfo[] cs = t2.GetConstructors();
    for (int i = 0; i < cs.Length; i++)
    {
        if (cs[i].GetParameters()[0].ParameterType == typeof(string))
        {
            return t.GetConstructors()[i];
        }
    }

    return null;
}

Не совсем точно, каково ваше намерение.

2 голосов
/ 15 мая 2009

Небольшое уточнение. Ни один из конструкторов не является универсальным методом. Это обычные методы в общем классе. Чтобы метод был «универсальным», он должен иметь универсальный параметр. Таким образом, выполнение теста типа «IsGenericMethod» вернет false.

Также непросто просто посмотреть на параметры и определить, являются ли они общими. Для примера, который вы дали, можно пройтись по аргументам и найти общий параметр. Но также рассмотрим следующий код

public Foo(IEnumerable<T> p1) ...
public Foo(IEnumerable<KeyValuePair<string,Func<T>>> p1) ...

Вам нужно будет принять во внимание подобные предметы.

EDIT

Причина, по которой вы видите все аргументы в виде строки, заключается в том, что вы явно связываете тип Foo до получения конструкторов. Попробуйте переключить свой код на следующий, который использует несвязанный Foo и, следовательно, будет возвращать общие параметры в методах.

var constructors = typeof( Foo<> ).GetConstructors();
0 голосов
/ 15 мая 2009

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

Я думаю, что цепочка конструкторов или построение логики в общую логику ведут себя определенным образом, если передаваемый параметр является строкой, такой как:

    static void Main(string[] args)
    {
        Console.WriteLine(new Foo<string>("myValue").IsValueAString);
        Console.WriteLine(new Foo<int>(1).IsValueAString);
        Console.ReadLine();
    }

    public class Foo<T>
    {
        public bool IsValueAString = false;
        public Foo(T value) {
            if (value is string)
            {
                IsValueAString = true;
            }
        }
    }

Другой вариант - создать конкретную реализацию Foo, что-то вроде:

internal class Foo<T>
{
    ...
}
internal class MyFoo : Foo<string>
{
    ...
}

и встраивать любую конкретную логику в конструктор потомка. Возможны всевозможные варианты по этому пути, поэтому вы можете избежать отображения информации из этого одного класса.

0 голосов
/ 15 мая 2009

Вы можете проверить Type.GetGenericArguments тип (ы) результата и сравнить его с типом параметра конструктора.

Просто вызовите тот, у которого тип не совпадает (type! = Typeof (T)).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...