Переопределение методов расширения LINQ

Существует ли способ переопределить дополнительные методы (обеспечьте лучшую реализацию), явно не имея необходимость бросить им? Я реализую тип данных, который может обработать определенные операции более эффективно, чем методы расширения по умолчанию, но я хотел бы сохранить общность IEnumerable. Тем путем любой IEnumerable может быть передан, но когда мой класс передается в, это должно быть более эффективно.

Как игрушечный пример, рассмотрите следующее:

// Compile: dmcs -out:test.exe test.cs

using System;

namespace Test {
    public interface IBoat {
        void Float ();
    }

    public class NiceBoat : IBoat {
        public void Float () {
            Console.WriteLine ("NiceBoat floating!");
        }
    }

    public class NicerBoat : IBoat {
        public void Float () {
            Console.WriteLine ("NicerBoat floating!");
        }

        public void BlowHorn () {
            Console.WriteLine ("NicerBoat: TOOOOOT!");
        }
    }

    public static class BoatExtensions {
        public static void BlowHorn (this IBoat boat) {
            Console.WriteLine ("Patched on horn for {0}: TWEET", boat.GetType().Name);
        }
    }

    public class TestApp {
        static void Main (string [] args) {
            IBoat niceboat = new NiceBoat ();
            IBoat nicerboat = new NicerBoat ();

            Console.WriteLine ("## Both should float:");
            niceboat.Float ();
            nicerboat.Float ();
            // Output:
            //      NiceBoat floating!
            //      NicerBoat floating!

            Console.WriteLine ();
            Console.WriteLine ("## One has an awesome horn:");
            niceboat.BlowHorn ();
            nicerboat.BlowHorn ();
            // Output:
            //      Patched on horn for NiceBoat: TWEET
            //      Patched on horn for NicerBoat: TWEET

            Console.WriteLine ();
            Console.WriteLine ("## That didn't work, but it does when we cast:");
            (niceboat as NiceBoat).BlowHorn ();
            (nicerboat as NicerBoat).BlowHorn ();
            // Output:
            //      Patched on horn for NiceBoat: TWEET
            //      NicerBoat: TOOOOOT!

            Console.WriteLine ();
            Console.WriteLine ("## Problem is: I don't always know the type of the objects.");
            Console.WriteLine ("## How can I make it use the class objects when the are");
            Console.WriteLine ("## implemented and extension methods when they are not,");
            Console.WriteLine ("## without having to explicitely cast?");
        }
    }
}

Существует ли способ получить поведение от второго случая без кастинга explict? Этой проблемы можно избежать?

5
задан Ruben Vermeersch 24 April 2010 в 19:38
поделиться

1 ответ

Методы расширения - это статические методы, и вы не можете переопределить статический метод. Вы также не можете «переопределить» фактический метод экземпляра с помощью статического метода / метода расширения.

Вам нужно будет явно использовать ваше оптимизированное расширение. Или неявно, ссылаясь на пространство имен вашего собственного расширения вместо System.Linq .

Или явно проверьте тип вашего расширения и вызовите правильный тип в зависимости от типа среды выполнения.

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

Я вижу много путаницы в этом аспекте методов расширения. Вы должны понимать, что это не миксины, они фактически не вводятся в класс. Это просто синтаксический сахар, который компилятор распознает и «позволяет» вам выполнять его, как если бы это был обычный метод экземпляра. Представьте, что это не метод расширения, а просто статический метод:

public static void BlowHorn (IBoat boat) {
    Console.WriteLine ("Patched on horn for {0}: TWEET", boat.GetType().Name);
}

Как бы вы «переопределили» этот метод из реализации IBoat ? Вы не можете. Единственное, что вы можете сделать, - это включить проверку типов в этот статический метод или написать код вызова динамического метода, используя блок dynamic в C # 4 или Reflection в более ранних версиях.

Чтобы прояснить ситуацию, взгляните на этот код из класса System.Linq.Enumerable из Reflector:

public static TSource ElementAt<TSource>(this IEnumerable<TSource> source, 
    int index)
{
    TSource current;
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
        IList<TSource> list = source as IList<TSource>;
    if (list != null)
    {
        return list[index];
    }
// ...
}

Это один из основных методов расширения в .NET Framework. Это позволяет оптимизировать, явно проверяя, реализует ли параметр IList . Кроме этого, у него нет возможности узнать, действительно ли базовый конкретный тип поддерживает индексированный доступ.Вам придется сделать это так же; создайте другой интерфейс, например IHorn или что-то в этом роде, и в своем расширении проверьте, реализует ли IBoat также IHorn , так же, как Enumerable ] класс делает здесь.

Если вы не контролируете код для классов IBoat или методов расширения, то вам не повезло. Если да, то используйте наследование с несколькими интерфейсами, явную проверку типов или динамический код - это ваши варианты.

15
ответ дан 18 December 2019 в 11:54
поделиться