Подтвердить что ты не робот

В С#, как я могу проверить, является ли T типом IInterface и отбрасывается, если мой объект поддерживает этот интерфейс?

В С# у меня есть функция, которая проходит через T с помощью generics, и я хочу запустить проверку, чтобы увидеть, является ли T object, которая реализует interface, и если это вызывает один из methods на этом interface.

Я не хочу иметь ограничения T только для этого типа. Можно ли это сделать?

Например:

public class MyModel<T> : IModel<T> where T : MyObjectBase
{
    public IQueryable<T> GetRecords()
    {
        var entities = Repository.Query<T>();
        if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
        {
            //Filterme is a method that takes in IEnumerable<IFilterable>
            entities = FilterMe(entities));
        }
        return entities;
    }

    public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
    {
        var dict = GetDict();
        return linked.Where(r => dict.ContainsKey(r.Id));
    }
 }

Ошибка, которую я получаю:

Ошибка 21 Тип "TResult" не может использоваться как параметр типа "TResult" в родовом типе или методе "FilterMe (System.Collections.Generic.IEnumerable)". Нет никакого неявного преобразования ссылок из "TResult" в "IFilterable".

4b9b3361

Ответ 1

Пропущенный фрагмент Cast<>():

if(typeof(IFilterable).IsAssignableFrom(typeof(T))) {
    entities = FilterMe(entities.Cast<IFilterable>()).AsQueryable().Cast<T>();
}

Обратите внимание на использование Cast<>() для преобразования списка сущностей в правильный подтип. Этот приведение не удастся, если T не выполнит IFilterable, но поскольку мы уже проверили это, мы знаем, что оно будет.

Ответ 2

if (typeof(IMyInterface).IsAssignableFrom(typeof(T))

Это проверяет, может ли быть назначена переменная типа IMyInterface из экземпляра типа T.

Ответ 3

Если у вас есть параметр общего типа, который может или не может реализовать IFoo, можно as передать его в хранилище типа IFoo; если вы это сделаете, вы можете передать его любому методу, который ожидает IFoo, а также любому методу, ожидающему общий параметр, ограниченный IFoo, но вы потеряете всю информацию об общем типе, если вы это сделаете - параметр будет передан как тип IFoo. Между прочим, это будет означать, что если ваш исходный объект был структурой, он будет помещен в коробку.

Если вы хотите проверить, реализует ли универсальный тип параметра IFoo и вызывает метод, который принимает ограниченный тип IFoo, если он это делает, сохраняя исходный общий тип (это может быть полезно, если тип является struct и может понадобиться, если тип передается в общий метод, который имеет как ограничения IFoo, так и IBar, и вещи, которые, возможно, захотят передать, не имеют общего общего супертипа), необходимо будет использовать Отражение.

Например, предположим, что нужно иметь метод Zap, который принимает общий параметр ref, вызывает на нем Dispose, если он реализует IDisposable, и очищает его. Если параметр является типом класса IDisposable, нулевой тест должен выполняться как атомная операция с очисткой параметра.

public static class MaybeDisposer
{
    static class ClassDisposer<T> where T : class,IDisposable
    {
        public static void Zap(ref T it)
        {
            T old_it = System.Threading.Interlocked.Exchange(ref it, null);
            if (old_it != null)
            {
                Console.WriteLine("Disposing class {0}", typeof(T));
                old_it.Dispose();
            }
            else
                Console.WriteLine("Class ref {0} already null", typeof(T));
        }
    }
    static class StructDisposer<T> where T : struct,IDisposable
    {
        public static void Zap(ref T it)
        { 
            Console.WriteLine("Disposing struct {0}", typeof(T));
            it.Dispose();
            it = default(T);
        }
    }
    static class nonDisposer<T>
    {
        public static void Zap(ref T it)
        {
            Console.WriteLine("Type {0} is not disposable", typeof(T));
            it = default(T);
        }
    }
    class findDisposer<T>
    {
        public static ActByRef<T> Zap = InitZap;
        public static void InitZap(ref T it)
        {
            Type[] types = {typeof(T)};
            Type t;
            if (!(typeof(IDisposable).IsAssignableFrom(typeof(T))))
                t = typeof(MaybeDisposer.nonDisposer<>).MakeGenericType(types);
            else if (typeof(T).IsValueType)
                t = typeof(MaybeDisposer.StructDisposer<>).MakeGenericType(types);
            else
                t = typeof(MaybeDisposer.ClassDisposer<>).MakeGenericType(types);
            Console.WriteLine("Assigning disposer {0}", t);
            Zap = (ActByRef<T>)Delegate.CreateDelegate(typeof(ActByRef<T>), t, "Zap");
            Zap(ref it);
        }
    }
    public static void Zap<T>(ref T it)
    {
        findDisposer<T>.Zap(ref it);
    }
}

При первом вызове кода с любым типом T он определит, какой родовой статический класс может быть создан для этого параметра, и используйте Reflection для создания делегата для вызова статического метода этого универсального класса. Последующие вызовы с тем же типом T будут использовать кешированный делегат. Хотя Reflection может быть немного медленным, его нужно будет использовать только один раз для любого типа T. Все последующие вызовы MaybeDisposer.Zap<T>(ref T it) с тем же типом T будут отправляться непосредственно через делегат и, таким образом, будут выполняться быстро.

Обратите внимание, что вызовы MakeGenericType будут генерировать исключения, если им заданы параметры типового типа, которые не соответствуют ограничениям открытых открытых классов (например, если T был классом или не реализовал IDisposable, попытка сделать общий тип StructDisposer<T> вызовет исключение); такие тесты выполняются во время выполнения и не проверяются компилятором, поэтому вы можете использовать проверку времени выполнения, чтобы увидеть, соответствуют ли типы соответствующим ограничениям. Обратите внимание, что код не проверяет, реализует ли it IDisposable, но вместо этого проверяет, выполняет ли это T. Это очень важно. В противном случае, если MaybeDispose был вызван с параметром типа Object, который содержал ссылку на Stream, он бы определил, что it реализован IDisposable и, таким образом, попытается создать ClassDisposer<Object>, сбой, потому что Object не реализует IDisposable.

Ответ 4

Простейшая форма, которую я могу придумать, - это что-то вроде:

public IEnumerable<T> GetRecords()
{
    IQueryable<T> entities = new List<T>().AsQueryable();

    if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
    {
        entities = FilterMe<IFilterable, T>(entities.OfType<IFilterable>()).AsQueryable();
    }

    return entities;
}

public IEnumerable<TResult> FilterMe<TSource, TResult>(IEnumerable<TSource> linked) where TSource : IFilterable
{
    return linked.Where(r => true).OfType<TResult>();
}

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

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

Поскольку вы переназначаете из FilterMe, вам по-прежнему нужна проверка назначаемого интерфейса.

Ответ 5

Является ли метод OfType(...) (ссылка), что вы ищете?

public class MyModel<T> : IModel<T> where T : MyObjectBase
{

    public IQueryable<T> GetRecords()
    {
        var entities = Repository.Query<T>();
        if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
        {
            //Filterme is a method that takes in IEnumerable<IFilterable>
            entities = FilterMe(entities));
        }
        return entities;
    }

     public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult :  IFilterable
    {
        var dict = GetDict();
        return linked.OfType<TResult>().Where(r => dict.ContainsKey(r.Id));
    }
}

Ответ 6

В моем ответе я предполагаю, что метод FilterMe используется внутри и не должен быть видимым вне вашей модели и может быть помечен private. Если мое предположение неверно, вы можете создать личную перегрузку FilterMe.

В моем ответе я просто удалил общий <TResult>. Я предполагаю, что этот FilterMe всегда относится к объектам типа T (так как он находится в одном классе модели). Это решает проблему литья между T и TResult. TResult не обязательно должен быть помечен как IFilterable, поскольку ни один из членов IFilterable не используется. И так как код уже проверяет, если T есть IFilterable, зачем снова проверять (особенно, когда FilterMe будет закрытым)?

    public IQueryable<T> GetRecords()
    {
        var entities = Repository.Query<T>();
        if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
        {
            //Filterme is a method that takes in IEnumerable<IFilterable>
            entities = FilterMe(entities).AsQueryable();
        }
        return entities;
    }

    public IEnumerable<T> FilterMe(IEnumerable<T> linked) 
    {
        var dict = GetDict();
        return linked.Where(r => dict.ContainsKey(r.Id));
    }

Если вы создадите второй FilterMe, замените типы IEumerable<T> на Queryable<T>, поэтому вам не нужно преобразовывать ваши объекты с помощью AsQueryable().

Ответ 7

public IEnumerable<TResult> FilterMe<TResult>(IEnumerable<TResult> linked) where TResult : IFilterable
{
    var dict = GetDict();
    return linked.Where(r => dict.ContainsKey(r.Id));
}

Попробуйте заменить FilterMe на эту версию:

public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked)
{
    var dict = GetDict();
    return linked.Where(r => dict.ContainsKey(r.Id)).Cast<T>();
}

Затем, если бы вы позвонили, измените свой код на это:

if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
{
    //Filterme is a method that takes in IEnumerable<IFilterable>
    var filterable = entities.Cast<IFilterable>();
    entities = FilterMe(entities).AsQueryable();
}

Ответ 8

Вам не нужно делать метод FilterMe универсальным методом для достижения того же результата.

    public class MyModel<T> : IModel<T> where T : MyObjectBase {
        public IQueryable<T> GetRecords()
    {
        var entities = Repository.Query<T>();
        if (typeof(IFilterable).IsAssignableFrom(typeof(T)))
        {
            //Filterme is a method that takes in IEnumerable<IFilterable>
            entities = FilterMe(entities.Cast<IFilterable>());
        }
        return entities;
    }

        public IEnumerable<T> FilterMe(IEnumerable<IFilterable> linked)  {
            var dict = GetDict();
            return linked
                    .Where(r => dict.ContainsKey(r.Id))
                    .Cast<T>();
        }
    }