我有一系列类型不定的对象,同时为每个对象提供了相应的方法,这个对象由一个方法在实时运算中提供,不确定,请问 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]);
}
1
wudicgi 2022-08-06 02:51:55 +08:00
用反射,argObj.GetType() 后去 argType.GetMethod() 获取到方法,再 Invoke?
https://stackoverflow.com/questions/32252555/methodinfo-invoke-for-overloaded-methods-in-c-sharp https://docs.microsoft.com/en-us/dotnet/api/system.type.getmethod |
2
wudicgi 2022-08-06 02:53:05 +08:00
如果类型不多的话,还是直接判断和调用比较好
|
3
wdc63 OP @wudicgi 谢谢,反射的性能开销大不大呢,会有大量的对象在循环调用这些方法。
而且类型太多了,几十种,如果用判断,那方法写起来太繁琐了。 |
4
wewewefff 2022-08-06 03:03:56 +08:00
突然想到热重载,有人解释一下吗
|
5
wudicgi 2022-08-06 03:11:32 +08:00
|
6
wdc63 OP @wudicgi 我能想到的就是靠参数类型去启动相应的方法,大概就是这么个意思:一个对象在一个场景中走,遇到 A 对象就和 A 对象产生互动,遇到 B 对象就和 B 对象互动。请问对于这种需求有没有更好的实现方法呢?
|
8
geelaw 2022-08-06 03:29:43 +08:00 via iPhone
你要找的是不是 visitor pattern ?
另外你的例子不好,因为内置类型是无法修改,但如果要考虑的类型是你的代码所控制的,则可以修改。 最后,如果按照你最开始的问题回答,你可以用 dynamic 。 |
9
dcsuibian 2022-08-06 03:37:19 +08:00
重载使用的方法好像是编译时就确定的吧。
|
10
Aloento 2022-08-06 03:53:47 +08:00
不想用 switch + match 的话(其实这个才是最好的方法)
就只能用反射了,反射开销极大 |
11
catcn 2022-08-06 08:37:15 +08:00
用泛型,抽取 interface ,用 interface 限制泛型
|
12
nebkad 2022-08-06 08:54:39 +08:00 via Android
需求没有表达得很清楚,我不是很理解你想做什么。不过看起来你只需要结合 delegate 就完事了,用参数类型作为 key 把一堆 delegate 保存在一个 dict 里面,然后实际作运行的时候检查 list 里面的每个元素的类型来查出 delegate
|
16
wdc63 OP @geelaw 看了 visitor pattern ,应该可行,但是感觉有点复杂了,比 switch 复杂得多,如果能用反射或者后面 delegate 的方法实现就好了。
|
18
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); } } } ``` 是要这样的? |
19
Aloento 2022-08-06 18:48:47 +08:00
@wdc63 反射为什么开销大...你每次调用的时候都要 VM 在整个 Class 里面查询匹配,然后动态生成代码,再执行,步骤太多了开销自然大
|
20
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 倍快 |
21
hez2010 2022-08-07 23:51:48 +08:00 via Android
|
22
neilq 2022-08-08 15:47:18 +08:00
// 简单用设计模式包裹了一下,工厂+策略,没测试,可以复制到 ide 方便看
public class HandlerKeyAttribute : Attribute { public HandlerKeyAttribute(string name) { Name = name; } public string Name { get; set; } } [HandlerKey(nameof(ArgA))] class ArgA { } [HandlerKey(nameof(ArgB))] class ArgB { } public class Message { public object Param { get; set; } } public interface IHandler { Task Handle(Message msg); } [HandlerKey(nameof(ArgA))] public class HandlerA : IHandler { public Task Handle(Message msg) { throw new NotImplementedException(); } } [HandlerKey(nameof(ArgB))] public class HandlerB : IHandler { public Task Handle(Message msg) { throw new NotImplementedException(); } } public class HandlerFactory { private static readonly Dictionary<string, Type> _handlerTypes = new(); static HandlerFactory() { var handlerTypeInfos = Assembly.GetAssembly(typeof(Program)).DefinedTypes .Where(x => x.IsClass && !x.IsAbstract && x.GetInterfaces().Any(i => i == typeof(IHandler))) .ToList(); foreach (var type in handlerTypeInfos) { var attr = type.GetCustomAttribute<HandlerKeyAttribute>(); if (attr != null && !string.IsNullOrEmpty(attr.Name)) { try { _handlerTypes.Add(attr.Name, type); } catch (Exception) { // 当心 key name 重复 } } } } public IHandler CreateHandler(object arg) { //简单用 attribute 匹配 arg 与 handler 类型,具体场景也可以用其他特征(如直接用对象名称)匹配,也可以结合正则、startsWith 等方式匹配 var attr = arg.GetType().GetCustomAttribute<HandlerKeyAttribute>(); if (attr == null || string.IsNullOrEmpty(attr.Name)) return null; if (!_handlerTypes.TryGetValue(attr.Name, out var handlerType)) return null; // 部分框架里可以利用 ioc container 创建, 如 return _serviceProvider.GetRequiredService(handlerType) as IMqttMessageHandler; // 部分场景也可以预创建对象,如_handlerTypes 类型改成 Dictionary<string, IHandler> handlers, 直接取拿出来用: return handlers[attr.name] return (IHandler)Activator.CreateInstance(handlerType); } } public class Program { private static void Main(string[] args) { var objects = new List<object> { new ArgA(), new ArgB() }; var factory = new HandlerFactory(); foreach (var arg in objects) { var handler= factory.CreateHandler(arg); handler.Handle(new Message { Param = arg }); } } } |