2012-04-26 5 views
6

У меня есть эти классы:C# несколько вариантов отправки?

class Asset 
{ } 

class House:Asset 
{ } 

считают эти посторонние, статические функции:

static void Foo (Asset a) { } 
static void Foo (House h) { } 

Если я пишу:

House h = new House (...); 
Foo(h); 

он будет вызывать Foo(House) (время компиляции связывания)

если я пишу:

Asset a = new House (...); 
Foo(a); 

он будет вызывать Foo(Asset) (время компиляции связывания)

Цель: получить доступ к методу типа выполнения:

У меня есть 2 варианта:

1) с использованием динамического, как это:

Asset a = new House (...); 
Foo ((dynamic)a); // NOW it will call Foo(House) 

2) переместите функции от static до override с использованием polymorphism mechanism.

вопрос:

есть ли другой способ сделать это (без перемещения функций polymorphism mechanism || dynamic)?

+0

Asset a = new House (...); не будет вызывать Foo (Asset), вызывать * runtime * type is House, поэтому он вызывается Foo (House) – Tigran

+6

@Tigran: Разрешение перегрузки выполняется во время компиляции. 'Asset a = новый дом (...); Foo (a); 'будет вызывать' Foo (Asset) ', потому что' a' объявляется как 'Asset'. – dtb

+0

@dtb: нет никакой перегрузки * это статические методы, и это будет * не * вызывать Foo (Asset), и даже если есть * значение * перегрузки, это идентификация типа времени выполнения и тип выполнения в строке Asset a = new House() - это дом. – Tigran

ответ

9

Цель: получить доступ к методу типа во время

Вот что dynamic ключевое слово там. На самом деле это довольно чистый & быстрый способ сделать несколько отправки.

Ваши конечные варианты Multiple Dispatch являются

  1. dynamic
  2. Двойные диспетчерские виртуальные методы
  3. Некоторые хэшированные анонимной функции коллекции правил
  4. if (x is House) ... else if(x is Asset)...
  5. Отражение - очень медленно и некрасиво

вопрос: есть ли другой способ сделать это (не перемещая функции к механизму полиморфизма || динамический)?

Так Да, есть способы сделать это займет много работы с вашей стороны, когда вы могли бы просто использовать dynamic, который быстро, менее подвержены ошибкам, и действительно clean syntax wise.

+0

Хороший компиляция возможных решений .. Спасибо .. У меня есть аналогичное решение, упомянутое в http://stackoverflow.com/questions/21261519/net-4-0-optimized-code-for-refactoring-existing-if-conditions-and- это-Operat/21284844 # 21284844 – Lijo

1

Я думаю, что это то, что под капотом Foo((dynamic)a) происходит:

Asset a = new House(); 

Type t = typeof(MainClass); 
t.InvokeMember("Foo", 
    System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { a }); 

Это будет решать Foo(House h)


Быстрая поездка в monodis.exe, без использования отражения (например, InvokeMember) , т.е. используя вместо этого динамическое ключевое слово, Asset a = new House(); Foo((dynamic)a), это IL:

IL_0025: ldstr "Foo" 
IL_002a: ldnull 
IL_002b: ldtoken MainClass 
IL_0030: call class [mscorlib]System.Type class [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) 

Довольно многое, что вам догадывается, скажет вам, что «Foo» - это мертвая распродажа, что динамика reflection-y вид бизнеса.

Теперь это без динамика, т.е. Asset a = new House(); Foo(a):

IL_0010: ldloc.0 
IL_0011: call void class MainClass::Foo(class Asset) 

Обожженной инструкция в значительной степени решил, не изменится, она всегда постановляет Foo(Asset);

Вот полный код, который вы можете использовать для анализировать динамическое поведение (через monodis.exe или ildasm.ехе):

using System; 

public class MainClass { 
    public static void Main() { 

     Console.WriteLine("Hei"); 

     Asset a = new House();   
     Foo(a);  
     Foo((dynamic)a); 

     object x = 7; 
     Foo((dynamic)x); 
    } 

    public static void Foo(House h) { Console.WriteLine("House"); } 
    public static void Foo(Asset a) { Console.WriteLine("Asset"); } 
    public static void Foo(int i) { Console.WriteLine("int"); } 
} 


public class Asset { 
} 

public class House : Asset { 
} 

Выход:

Hei 
Asset 
House 
int 

Это будет вызывать перегрузки Int Foo, т.е. Foo(int i):

object x = 7; 
t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { x }); 

Это бы тоже:

t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, 
    t, new object[] { 8 }); 

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

public static void FooDynamic(object o) 
{ 
    Type t = typeof(MainClass); 
    t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { o }); 
} 

Для вызова:

Asset a = new House(); 
FooDynamic(a); // will select Foo House overload 

int i = 7; 
FooDynamic(i); // will select Foo int overload 

Вы также можете использовать этот API для код выше: public static void Foo(object o), то вам придется позвонить Foo, как это:

Asset a = new House(); 
Foo((object)a); // will resolve to House 

Учитывая, что уже есть возможность dynamic в C# 4, я бы с трудом решился использовать отражение, если Дев не все еще использует C# 3. Так что, использовать динамический подход вместо :-)


UPDATE

за то, что это стоит, dynamicslow (по крайней мере, на моно), когда я запускаю этот код, перед появлением буквы «B» появляется около 2 секунд. Задержка динамика воспроизводима, даже я меняю порядок кода динамического и отражения. Задержка отражения незаметна, она быстрее динамической.

using System; 

public class MainClass { 

    public static void Main() { 

     // there's a delay on initial dynamic call, about two seconds 
     Test(); 
     Console.ReadLine(); 

     // dynamic's speed is instant on subsequent calls, 
     // as clarified by Eric Lippert, the delegate is cached, 
     // hence the elimination of delay on subsequent dynamic calls 
     Test(); 

    } 

    public static void Test() { 

     Asset a = new House(); 

     Console.WriteLine("A"); 
     Foo((dynamic)a); // there is a considerable delay here, the "B" string appears after two seconds 

     Console.WriteLine ("B");   
     Type t = typeof(MainClass); 
     t.InvokeMember("Foo", System.Reflection.BindingFlags.InvokeMethod, null, t, new object[] { a }); 

     Console.WriteLine("C"); 

    } 


    public static void Foo(House h) { Console.WriteLine("House"); } 
    public static void Foo(Asset a) { Console.WriteLine("Asset"); } 
    public static void Foo(int i) { Console.WriteLine("int"); } 
} 


public class Asset { 
} 

public class House : Asset { 
} 
+0

Под капотом это делает сумасшедшие вещи, которые компилятор делает для решения статических методов, но с типом среды выполнения, компиляции на лету и кэширование его для будущего использования. Фактически, ваш пример отражения не будет работать с перегруженными методами, ваш недостающий связующий, используемый для выбора правильной перегрузки. – jbtule

+0

au contrarie, он работает :-) InvokeMember заботится о бизнесе, из которого перегружен метод для вызова. См. Мое редактирование –

+1

Извините, что вы правы, InvokeMember работает в этом случае, это более сложные случаи, в которых это не так. Но 'Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember', который компилятор фактически свопирует, когда вы используете' dynamic' обрабатывает больше случаев, чем этот метод отражения, и испускает IL во время выполнения, чтобы сделать его иногда до 100 быстрее, чем отражение. – jbtule

1

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

Я также предлагаю игнорировать всех, кто говорит, что синглтон - это ужасно, плохо. Prima Donna singleton woe-проповедь - это shibboleth разработчиков среднего уровня. Синглтон - это просто инструмент, и если он соответствует вашим потребностям, то используйте его.

Смежные вопросы