Что ж, один из вариантов — просто перегрузить метод Fix()
, чтобы у вас была одна перегрузка для каждого типа, реализующего IFoo
. Но я подозреваю, что вы хотите принять интерфейс напрямую, а не реализовывать типы.
На самом деле вам нужна множественная отправка. Обычно C# /VB использует типы аргументов для выполнения разрешения перегрузки во время компиляции и динамической отправки вызова на основе типа среды выполнения экземпляра, для которого вызывается метод. Что вам нужно, так это выполнять разрешение перегрузки во время выполнения на основе времени выполнения типов аргументов — функция, которая напрямую не поддерживается ни в VB.NET, ни в C#.
Раньше я обычно решал такие проблемы, используя словарь делегатов, проиндексированный System.Type
:
private readonly Dictionary<Type,Action<IFoo>> _dispatchDictionary;
static Bar()
{
_dispatchDictionary.Add( typeof(TextFoo), DoBarTextFoo );
_dispatchDictionary.Add( typeof(AudioFoo), DoBarAudioFoo );
_dispatchDictionary.Add( typeof(VideoFoo), DoBarVideoFoo );
}
public void Fix( IFoo foo )
{
Action<IFoo> barAction;
if( _dispatchDictionary.TryGetValue( foo.GetType(), out barAction ) )
{
barAction( foo );
}
throw new NotSupportedException("No Bar exists for type" + foo.GetType());
}
private void DoBarTextFoo( IFoo foo ) { TextFoo textFoo = (TextFoo)foo; ... }
private void DoBarAudioFoo( IFoo foo ) { AudioFoo textFoo = (AudioFoo)foo; ... }
private void DoBarVideoFoo( IFoo foo ) { VideoFoo textFoo = (VideoFoo)foo; ... }
Однако, начиная с C# 4, теперь мы можем использовать ключевое слово dynamic
в C#, по существу делая то же самое (VB.NET пока не имеет этой функции):
public void Fix( IFoo foo )
{
dynamic dynFoo = foo;
dynamic thisBar = this;
thisBar.DoBar( dynFoo ); // performs runtime resolution, may throw
}
private void Dobar( TextFoo foo ) { ... /* no casts needed here */ }
private void Dobar( AudioFoo foo ) { ... }
private void Dobar( VideoFoo foo ) { ... }
Обратите внимание, что использование ключевого слова dynamic
таким образом имеет свою цену — требуется, чтобы сайт вызова обрабатывался во время выполнения. По сути, он запускает версию компилятора C# во время выполнения, обрабатывает метаданные, захваченные компилятором, выполняет анализ типов во время выполнения и выдает код C#. К счастью, DLR обычно может эффективно кэшировать такие сайты вызовов после их первого использования.
Как правило, я нахожу оба эти шаблона запутанными и излишними в большинстве ситуаций. Если количество подтипов невелико и все они известны заранее, простой блок if/else
может оказаться более полезным. проще и понятнее.
person
LBushkin
schedule
24.03.2011