Можно ли повторно использовать универсальный тип для вызова метода с дополнительными ограничениями? - PullRequest
0 голосов
/ 14 февраля 2011

Пример 1

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

public static TTarget ConvertValue<TTarget>(object value)
{
    Type t = typeof(TTarget);
    if (t.IsEnum)
        return ParseEnum<TTarget>(value);
    else //if ...
        return ...;
}

Некоторые методы, которые обрабатывают значение, имеют параметр общего типа с ограничениями.

public static TEnum ParseEnum<TEnum>(object value)
    where TEnum : struct
{
    //do something
    return ...;
}

Компилятор не допускает такой подход, поскольку тип TTarget не обязательно является struct и может стать NULL и, следовательно, не может использоваться как TEnum.

Пример 2

Предположим, что имеется универсальный метод без ограничений и метод с дополнительными ограничениями:

public void DoStuff<T>(T obj)
{
    if (obj is IComparable && obj is ICloneable)
        DoSpecialStuff<T>(obj);
}
public void DoSpecialStuff<T>(T obj)
    where T : IComparable, ICloneable
{
}

Это также не работает, потому что (afaik) нет возможности привести к нескольким интерфейсам.

Можно ли повторно использовать универсальный тип для вызова метода с дополнительными ограничениями?

Ответы [ 3 ]

3 голосов
/ 14 февраля 2011

Вам нужно вызвать метод, используя Reflection.
Нет лучшего способа.

Вам следует вместо этого вызывать неуниверсальный метод (и передавать typeof(TTarget) в качестве параметра) - ParseEnum не должен быть универсальным.

2 голосов
/ 14 февраля 2011

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

public static TTarget ConvertValue<TTarget>(this object value)
{
    Type t = typeof(TTarget);

    if (t.IsEnum)
    {
        Delegate del = _delegateCache.GetOrAdd(t, t2 =>
            Delegate.CreateDelegate(typeof(Func<object, TTarget>),
                                    _parseEnumInfo.MakeGenericMethod(t2));
        return ((Func<object, TTarget>)del)(value);
    }
    else // if ...
        return ...;
}

private static readonly MethodInfo _parseEnumInfo =
    typeof(YourParentClass).GetMethod("ParseEnum");

private static readonly ConcurrentDictionary<Type, Delegate> _delegateCache =
    new ConcurrentDictionary<Type, Delegate>();

public static TEnum ParseEnum<TEnum>(object value)
    where TEnum : struct, IComparable, IConvertible, IFormattable
{
    // do something
    return ...;
}

Или, чтобы соответствовать вашему второму примеру:

public void DoStuff<T>(T obj)
{
    if ((obj is IComparable) && (obj is ICloneable))
    {
        Delegate del = _delegateCache.GetOrAdd(typeof(T), t =>
            Delegate.CreateDelegate(typeof(Action<T>),
                                    this,
                                    _doSpecialStuffInfo.MakeGenericMethod(t));
        ((Action<T>)del)(obj);
    }
}

private static readonly MethodInfo _doSpecialStuffInfo =
    typeof(YourParentClass).GetMethod("DoSpecialStuff");

private readonly ConcurrentDictionary<Type, Delegate> _delegateCache =
    new ConcurrentDictionary<Type, Delegate>();

public void DoSpecialStuff<T>(T obj)
    where T : IComparable, ICloneable
{
}
0 голосов
/ 23 ноября 2011

Если для каждого интересующего интерфейса вы определяете версию с «дополнительным» универсальным параметром T, который наследует как универсальный интерфейс, так и ISelf (я бы предложил, чтобы интерфейс содержал единственное свойство только для чтения «self») типа T), и если каждый интересующий класс реализует ISelf , то если, например, IFoo наследуется от IFoo и T, подпрограммы, которой требуется что-то для реализации, как IFoo, так и IBar могут принимать параметр типа IFoo . Если переданный параметр называется X, X будет реализовывать IFoo, а X.Self будет реализовывать IBar. Прекрасной особенностью этого подхода является то, что любой такой объект, реализующий любую комбинацию интерфейсов, определенных таким образом, может быть приведен по типу к чему-то, что будет реализовывать любую комбинацию этих интерфейсов в любом порядке. Недостаток этого подхода в том, что он предполагает, что объекты будут определять ISelf .Self, чтобы возвращать себя, но нет реальной гарантии, что такой объект не будет возвращать что-то еще.

...