Существует ли способ переопределить дополнительные методы (обеспечьте лучшую реализацию), явно не имея необходимость бросить им? Я реализую тип данных, который может обработать определенные операции более эффективно, чем методы расширения по умолчанию, но я хотел бы сохранить общность 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? Этой проблемы можно избежать?
Методы расширения - это статические методы, и вы не можете переопределить статический метод. Вы также не можете «переопределить» фактический метод экземпляра с помощью статического метода / метода расширения.
Вам нужно будет явно использовать ваше оптимизированное расширение. Или неявно, ссылаясь на пространство имен вашего собственного расширения вместо 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
или методов расширения, то вам не повезло. Если да, то используйте наследование с несколькими интерфейсами, явную проверку типов или динамический код - это ваши варианты.