我应该如何用 C#方法重载实现这样的目的

2022-08-06 02:44:35 +08:00
 wdc63

我有一系列类型不定的对象,同时为每个对象提供了相应的方法,这个对象由一个方法在实时运算中提供,不确定,请问 C#中有没有方法如何利用重载或其他手段来实现对象被输入给相应的方法,例如实现下面这种,但是当然下面这种写法会报错。

不太想用判断,写起来太繁琐了。

    private static void test(string str)
    {
        Console.WriteLine(str);
    }

    private static void test(int i)
    {
        Console.WriteLine(i);
    }

    private static void Main(string[] args)
    {
        var a = new List<object>() { 123, "asd" };
        test(a[0]);
        test(a[1]);
    }
2083 次点击
所在节点    C#
23 条回复
wudicgi
2022-08-06 02:51:55 +08:00
wudicgi
2022-08-06 02:53:05 +08:00
如果类型不多的话,还是直接判断和调用比较好
wdc63
2022-08-06 03:00:23 +08:00
@wudicgi 谢谢,反射的性能开销大不大呢,会有大量的对象在循环调用这些方法。
而且类型太多了,几十种,如果用判断,那方法写起来太繁琐了。
wewewefff
2022-08-06 03:03:56 +08:00
突然想到热重载,有人解释一下吗
wudicgi
2022-08-06 03:11:32 +08:00
@wdc63 一定要靠参数类型以类似方法重载的方式调用吗?
每个调用如果事先存储好方法名,或者可以查到方法名,就可以直接调用了

运行时靠参数类型去找方法的重载有点奇怪
wdc63
2022-08-06 03:18:55 +08:00
@wudicgi 我能想到的就是靠参数类型去启动相应的方法,大概就是这么个意思:一个对象在一个场景中走,遇到 A 对象就和 A 对象产生互动,遇到 B 对象就和 B 对象互动。请问对于这种需求有没有更好的实现方法呢?
wdc63
2022-08-06 03:21:16 +08:00
@wudicgi 另外 A 、B 对象的行为差距很大的,有几十个这种对象类,没法用一个接口来实现。
geelaw
2022-08-06 03:29:43 +08:00
你要找的是不是 visitor pattern ?

另外你的例子不好,因为内置类型是无法修改,但如果要考虑的类型是你的代码所控制的,则可以修改。

最后,如果按照你最开始的问题回答,你可以用 dynamic 。
dcsuibian
2022-08-06 03:37:19 +08:00
重载使用的方法好像是编译时就确定的吧。
Aloento
2022-08-06 03:53:47 +08:00
不想用 switch + match 的话(其实这个才是最好的方法)
就只能用反射了,反射开销极大
catcn
2022-08-06 08:37:15 +08:00
用泛型,抽取 interface ,用 interface 限制泛型
nebkad
2022-08-06 08:54:39 +08:00
需求没有表达得很清楚,我不是很理解你想做什么。不过看起来你只需要结合 delegate 就完事了,用参数类型作为 key 把一堆 delegate 保存在一个 dict 里面,然后实际作运行的时候检查 list 里面的每个元素的类型来查出 delegate
wdc63
2022-08-06 15:36:43 +08:00
@nebkad 谢谢,请问您能不能举个例子呢?
wdc63
2022-08-06 15:37:00 +08:00
@catcn 不太懂这种方法。
wdc63
2022-08-06 15:37:36 +08:00
@nebkad 需求就是一个对象在运行过程中会随机遇到另一个对象,然后触发相应的方法。
wdc63
2022-08-06 15:39:31 +08:00
@geelaw 看了 visitor pattern ,应该可行,但是感觉有点复杂了,比 switch 复杂得多,如果能用反射或者后面 delegate 的方法实现就好了。
wdc63
2022-08-06 15:40:09 +08:00
@Aloento 谢谢,反射为什么开销很大呢。
Al0rid4l
2022-08-06 17:28:47 +08:00
```C#
public class Program {
public static void Test(object param) {
Console.WriteLine(
param switch {
int a => a,
string b => b,
_ => throw new ArgumentException("error")
}
);
}

public static void Main() {
var arr = new List<object> { "abc", 123 };
foreach (object item in arr) {
Test(item);
}
}
}
```

是要这样的?
Aloento
2022-08-06 18:48:47 +08:00
@wdc63 反射为什么开销大...你每次调用的时候都要 VM 在整个 Class 里面查询匹配,然后动态生成代码,再执行,步骤太多了开销自然大
Zhuzhuchenyan
2022-08-07 03:20:02 +08:00
如果不想写繁琐的 switch ,同时也想避免反射带来的开销,考虑一下 Delegate.CreateDelegate 来创建一个可以重复使用的 Delegate

https://gist.github.com/Charles-YYH/97785d39b72bef2df1b639a5e0081289
我放了一个示例在这里,仓促之中写完,这个示例有不少可以优化的地方
1. ProcessInt 和 ProcessString 应该是可以避免使用 object 来作为形参的类型,但是既然你题干中把值类型和引用类型都放在同一个 List 里,说明此处装箱应该是可以接受的
2. 如果觉得 Dictionary 查找太慢,可以考虑使用表达式树来达到和 switch 相同数量级的的性能


相关资料
1. Delegate.CreateDelegate 性能研究: https://stackoverflow.com/a/16078960/8877198 ,大概是普通反射的 30 倍快

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/871010

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX