更新
请求重新打开,因为其他SO答案没有解决方案,但问题的其中一条评论有一个我想接受的解决方案,因为它适用于该方案。
原始问题
我在使用非抽象基类和选择适当扩展方法的子类编写扩展方法时遇到了麻烦。
我在下面有一个非常简单的例子(从一个更大的项目中抽象出来),它使用了扩展方法" Run"。预期的输出列在每个类旁边的注释中。
public class Parent { }; // Should output "Parent"
public class ChildA : Parent { }; // Should output "Child A"
public class ChildB : Parent { }; // Should output "Parent"
// Expected Output: ChildA, Parent, Parent
public class Program
{
public static void Main()
{
var commands = new List<Parent>() { new ChildA(), new ChildB(), new Parent() };
Console.WriteLine(string.Join(", ", commands.Select(c => c.Run())));
}
}
到目前为止,这是我的尝试,但必须采用更简洁的方法:
尝试列表
public static class Extensions
{
public static string Run(this ChildA model)
{
return "ChildA";
}
public static string Run(this Parent model)
{
return model.Run1(); // Change to test different approaches
}
public static string Run1(this Parent model) // No type-checking
{
return "Parent";
}
public static string Run2(this Parent model) // Explicitly check sub-types
{
if (model is ChildA)
return ((ChildA)model).Run();
else
return "Parent";
}
public static string Run3(this Parent model) // Attempted dynamic type conversion
{
if (model.GetType().BaseType == typeof(Parent))
{
dynamic changedObj = Convert.ChangeType(model, model.GetType());
return changedObj.Run();
}
else
return "Parent";
}
public static string Run4(this Parent model) // Attempted reflected generic type conversion
{
if (model.GetType().BaseType == typeof(Parent))
{
var method = typeof(Extensions).GetMethod("Cast");
var generic = method.MakeGenericMethod(new[] { model.GetType() });
//var generic = generic.Invoke(new object(), null);
//return generic.Run();
return "Not working yet";
}
else
return "Parent";
}
public static T Cast<T>(this object input)
{
return (T) input;
}
}
答案 0 :(得分:2)
为Parent
和ChildA
创建两个扩展方法,您可以使用dynamic
将关联移动到运行时。
Console.WriteLine(string.Join(", ", commands.Select(c => Extensions.Run(c as dynamic))));
答案 1 :(得分:1)
最好的Run方法重载在编译时解决,对于List<Parent>
的项目是Run(this Parent model)
。可以使用扩展方法中的反射来模仿多态行为
using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
public static class Extensions
{
private static Dictionary<Type, MethodInfo> _runs;
private static Type _parentType;
static Extensions()
{
_parentType = typeof(Parent);
_runs = new Dictionary<Type, MethodInfo>();
// overloads of Run method, which return string for different types derived from Parent
var methods = typeof(Extensions)
.GetMethods(BindingFlags.Static|BindingFlags.Public)
.Where(m => m.Name == "Run" && m.ReturnType == typeof(string));
foreach(var mi in methods)
{
var args = mi.GetParameters();
// method should have only one parameter
if (args.Length != 1 || _parentType.IsAssignableFrom(args[0].ParameterType) == false)
return;
_runs.Add(args[0].ParameterType, mi);
}
}
//重载
public static string Run(this ChildA model)
{
return "ChildA";
}
public static string Run(this Parent model, object args)
{
// this method is not added to _runs (2 parameters)
return null;
}
public static int Run(this ChildC model)
{
// this method is not added to _runs (return int)
return 0;
}
public static string Run(this Parent model) // Attempted dynamic type conversion
{
// not really correct
if (model == null)
return "Parent";
var t = model.GetType();
if (t == _parentType)
return "Parent";
// invoke overload for type t
if (_runs.ContainsKey(t))
return (string) _runs[t].Invoke(null, new object[] {model});
return "Not working yet";
}
}
//用法
public class Parent { }; // Should output "Parent"
public class ChildA : Parent { }; // Should output "Child A"
public class ChildB : Parent { }; // Should output "Not working yet"
public class ChildC : Parent { };
public class Program
{
public static void Main()
{
var commands = new List<Parent>() { new ChildA(), new ChildB(), new Parent(), new ChildC(), (ChildA)null};
Console.WriteLine(string.Join(", ", commands.Select(c => c.Run())));
// extension method can be invoked for null
Console.WriteLine(((ChildA)null).Run());
//// crashes on (ChildA)null with error:
//// The call is ambiguous between the following methods or properties: 'Extensions.Run(ChildA)' and 'Extensions.Run(ChildC)'
//Console.WriteLine(string.Join(", ", commands.Select(c => Extensions.Run(c as dynamic))));
}
}
恢复
扩展方法可以作为常用方法(.Run()
)调用,而不是作为静态Extensions.Run
扩展方法Run(this Parent model)
存在null
参数问题(无法解析类型正确)
dynamic
的技巧在大多数情况下有效,但是:
int Run(this ChildC model)
方法,该方法会像其他人一样返回int
而非string
(从列表中删除(ChildA)null
时)The call is ambiguous between the following methods or properties: 'Extensions.Run(ChildA)' and 'Extensions.Run(ChildC)'
错误而崩溃(我不明白)