带有 base 和 sub-类 的扩展方法
Extension methods with base and sub-classes
更新
请求重新打开,因为其他 SO 答案没有解决方案,但对该问题的评论之一有一个我想接受的解决方案,因为它适用于该场景。
原题
我在使用非抽象基础 classes 和子 classes 编写扩展方法时遇到问题 select 适当的扩展方法。
我在下面有一个非常简单的示例(从一个更大的项目中抽象出来),它使用了扩展方法 "Run"。预期输出列在每个 class.
旁边的注释中
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())));
}
}
到目前为止,这是我的尝试,但必须有一种更简洁的方法来做到这一点:
- 无类型检查 - 以独占方式使用 Parent 扩展方法(Parent、Parent、Parent)
- 显式类型检查 - 正确的输出,但必须为每种扩展可能性(ChildA、Parent、Parent)显式检查类型
- 尝试 Convert.ChangeType 动态类型 - 运行 次异常,因为扩展不会捕获动态类型(无输出)
- 尝试使用反射进行通用转换 - 还不是很实用,但不确定方法是否有效
尝试列表
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;
}
}
为 Parent
和 ChildA
创建两个扩展方法,您可以使用 dynamic
将关联移动到运行时。
Console.WriteLine(string.Join(", ", commands.Select(c => Extensions.Run(c as dynamic))));
最好的 运行 方法重载在编译时解决,对于 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)
方法,returns int
不像其他方法 string
(当 (ChildA)null
从列表中删除时)
- 崩溃并出现
The call is ambiguous between the following methods or properties: 'Extensions.Run(ChildA)' and 'Extensions.Run(ChildC)'
错误(我不明白)
更新
请求重新打开,因为其他 SO 答案没有解决方案,但对该问题的评论之一有一个我想接受的解决方案,因为它适用于该场景。
原题
我在使用非抽象基础 classes 和子 classes 编写扩展方法时遇到问题 select 适当的扩展方法。
我在下面有一个非常简单的示例(从一个更大的项目中抽象出来),它使用了扩展方法 "Run"。预期输出列在每个 class.
旁边的注释中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())));
}
}
到目前为止,这是我的尝试,但必须有一种更简洁的方法来做到这一点:
- 无类型检查 - 以独占方式使用 Parent 扩展方法(Parent、Parent、Parent)
- 显式类型检查 - 正确的输出,但必须为每种扩展可能性(ChildA、Parent、Parent)显式检查类型
- 尝试 Convert.ChangeType 动态类型 - 运行 次异常,因为扩展不会捕获动态类型(无输出)
- 尝试使用反射进行通用转换 - 还不是很实用,但不确定方法是否有效
尝试列表
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;
}
}
为 Parent
和 ChildA
创建两个扩展方法,您可以使用 dynamic
将关联移动到运行时。
Console.WriteLine(string.Join(", ", commands.Select(c => Extensions.Run(c as dynamic))));
最好的 运行 方法重载在编译时解决,对于 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)
方法,returnsint
不像其他方法string
(当(ChildA)null
从列表中删除时) - 崩溃并出现
The call is ambiguous between the following methods or properties: 'Extensions.Run(ChildA)' and 'Extensions.Run(ChildC)'
错误(我不明白)