通用过载解决方案

发布于 2024-09-18 07:08:47 字数 593 浏览 5 评论 0原文

我有以下场景:

class Foo { }

class Foo<T> : Foo { }

然后有两个方法

void DoStuff(Foo foo) 
{
     DoStuffImpl(foo);
}

void DoStuffImpl(Foo foo) 
{ 
     Console.WriteLine("A");
}    
void DoStuffImpl<T>(Foo<T> foo) 
{ 
     Console.WriteLine("B");
} 

void Main() 
{
     DoStuff(new Foo<int>()); // prints A
}

(注意,代码是在浏览器中编写的,但描述了我面临的情况)

如何让它调用通用方法并打印 B?

这是否可以在不反思的情况下完成?我对如何通过反射来完成它有一些想法,但我正在寻找一种更干净的解决方案(如果存在)。

注意:我无法使 DoStuff 泛型,因为它将与 WCF 一起使用,并且不允许开放泛型类型。

I have the following scenario:

class Foo { }

class Foo<T> : Foo { }

And then two methods

void DoStuff(Foo foo) 
{
     DoStuffImpl(foo);
}

void DoStuffImpl(Foo foo) 
{ 
     Console.WriteLine("A");
}    
void DoStuffImpl<T>(Foo<T> foo) 
{ 
     Console.WriteLine("B");
} 

void Main() 
{
     DoStuff(new Foo<int>()); // prints A
}

(note, the code was written in the browser, but describes the situation I'm facing)

How can I get it to call the generic method, and print B?

Can this be done at all without reflection? I have some ideas on how it could be done with reflection, but I'm looking for a cleaner solution if one exists.

Note: I can't make DoStuff generic because this will be used with WCF and open generic types are not allowed.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

变身佩奇 2024-09-25 07:08:47

(我假设您已经了解为什么会发生这种情况。如果没有,请阅读我的重载解决文章如果仍然不清楚,请告诉我。)

如果您使用 C# 4,您可以使用动态类型:

void DoStuff(Foo foo) 
{
    dynamic d = foo;
    DoStuffImpl(d);
}

请注意,这不仅仅是一个动态参数 - 这个想法是通过限制 foo 属于 Foo 类型或子类,我们总是有一个有效的 DoStuffImpl 来调用...它是只是最好的方法将在执行时确定,而不是编译时。

如果您陷入 C# 4 之前的版本,您可以使用双重调度来实现:

class Foo
{
    public virtual void CallStuffImpl(FooImplType x)
    {
        x.DoStuffImpl(this);
    }
}

class Foo<T> : Foo
{
    public override void CallStuffImpl(FooImplType x)
    {
        // Looks like it's redundant, but isn't! "this" is
        // known to be Foo<T> rather than Foo
        x.DoStuffImpl(this);
    }
}

然后:

void DoStuff(Foo foo) 
{
    foo.CallStuffImpl(this); // Let it dispatch appropriately
}

(I assume you already understand why this is happening. If not, read my overload resolution article and let me know if it's still unclear.)

If you're using C# 4 you could use dynamic typing:

void DoStuff(Foo foo) 
{
    dynamic d = foo;
    DoStuffImpl(d);
}

Note how this doesn't just have a dynamic parameter - the idea is that by restricting foo to be of type Foo or a subclass, we'll always have a valid DoStuffImpl to call... it's just that the best method will be determined at execution time, not compile time.

If you're stuck in pre-C# 4, you could potentially do it with double dispatch:

class Foo
{
    public virtual void CallStuffImpl(FooImplType x)
    {
        x.DoStuffImpl(this);
    }
}

class Foo<T> : Foo
{
    public override void CallStuffImpl(FooImplType x)
    {
        // Looks like it's redundant, but isn't! "this" is
        // known to be Foo<T> rather than Foo
        x.DoStuffImpl(this);
    }
}

Then:

void DoStuff(Foo foo) 
{
    foo.CallStuffImpl(this); // Let it dispatch appropriately
}
白衬杉格子梦 2024-09-25 07:08:47

重载解析在编译时执行。当“DoStuff”被编译时,它已经决定要调用哪个版本的DoStuffImpl,并且它是根据编译时可用的信息而不是运行时可用的信息来决定的。

C# 中有四种方法分派:

  • 静态分派在编译时选择静态方法。在运行时,将调用所选择的方法。

  • 实例分派在编译时选择实例方法。在运行时,调用所选方法。 (这是在非虚拟实例方法和使用“base”调用的虚拟方法上使用的分派形式。)

  • 虚拟分派在编译时选择实例方法。在运行时,调用对象的运行时类型上该方法的最重写版本

  • 动态调度在编译时不执行任何操作 (*)。在运行时编译器会再次启动并在运行时进行编译时分析,并生成全新的代码,如果您一开始就在编译时正确编写了该代码,则会生成新的代码。听起来确实很贵;幸运的是,结果被缓存,这样在第二次调用时,您就不会再次获得分析和代码生成的所有成本。

动态调度仅适用于 C# 4 或任何版本的 VB。

(*) 这并不完全正确;在某些情况下,即使方法的参数是动态的,编译器也可以在编译时进行分析。细节很复杂。

Overload resolution is performed at compile time. When "DoStuff" is compiled, it has already decided which version of DoStuffImpl to call, and it decides that based on the information available at compile time, not the information available at runtime.

There are four kinds of method dispatching in C#:

  • static dispatching chooses a static method at compile time. At runtime, the chosen method is called.

  • instance dispatching chooses an instance method at compile time. At runtime, the chosen method is called. (This is the form of dispatching used on non-virtual instance methods and on virtual methods called with "base.")

  • virtual dispatching chooses an instance method at compile time. At runtime, the most overridding version of that method on the runtime type of the object is called.

  • dynamic dispatching does nothing at compile time (*). At runtime the compiler starts up again and does the compile-time analysis at runtime, and generates fresh new code that is what would have been written had you gotten it right at compile time in the first place. This is every bit as expensive as it sounds; fortunately the result is cached so that on the second call, you don't get all the cost of analysis and codegen again.

Dynamic dispatching is only available in C# 4, or any version of VB.

(*) This is not quite true; there are some circumstances in which the compiler can do analysis at compile time even if the arguments to the method are dynamic. The details are complicated.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文