6

我有这些课程:

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) (编译时绑定)

目标:访问运行时类型方法:

我有两个选择:

1)像这样使用动态:

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

2) 将功能从usingstatic移至overrideusing polymorphism mechanism

问题

还有其他方法吗(无需将函数移至polymorphism mechanism|| dynamic)?

4

3 回答 3

9

目标:访问运行时类型方法

这就是dynamic关键字的用途。这实际上是一种非常干净且快速的多次调度方式。

您对多次调度的最终选择是

  1. dynamic
  2. 双分派虚方法
  3. 一些散列匿名函数规则集合
  4. if (x is House) ... else if(x is Asset)...
  5. 反射——真的很慢很丑

问题:还有其他方法吗(不将函数移至多态机制||动态)?

dynamic所以,是的,当您可以使用快速、不易出错且语法非常简洁的方法时,有一些方法需要您做很多工作。

于 2012-04-26T13:09:10.163 回答
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”是一个死的赠品,动态是一种反射式的业务。

现在,这是动态的,即Asset a = new House(); Foo(a)

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

烧毁的指令几乎是确定的,不会改变,它总是解决Foo(Asset);

以下是可用于分析动态行为的完整代码(通过 monodis.exe 或 ildasm.exe):

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

这将调用 Foo 重载 int,即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

鉴于 C# 4 中已经有一项dynamic功能,除非开发人员仍在使用 C# 3,否则我将很难使用反射。因此,请改用动态方法:-)


更新

对于它的价值,它dynamic(至少在 Mono 上),当我运行这段代码时,在字母“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 {
}
于 2012-04-26T12:49:30.663 回答
1

如果您想要静态入口点,但也想要多态行为,那么最简单的混合是使用单例模式。它将为您提供静态入口点,并且它返回的对象将具有多态方法。

我还建议忽略所有说单身是一件可怕的坏事的人。Prima Donna 单例讲道是中级开发人员的陈词滥调。单例只是一个工具,如果它符合您的需求 - 那就使用它。

于 2012-04-26T13:34:08.910 回答