C# 具有基类和子类的扩展方法
更新 请求重新打开,因为另一个SO答案没有解决方案,但问题的一个注释有一个解决方案,我想接受,因为它适用于该场景 原始问题 我很难用非抽象基类和选择适当扩展方法的子类编写扩展方法 下面我有一个非常简单的例子(从一个更大的项目中抽象出来),它使用扩展方法“Run”。预期输出列在每个类旁边的注释中C# 具有基类和子类的扩展方法,c#,inheritance,reflection,extension-methods,C#,Inheritance,Reflection,Extension Methods,更新 请求重新打开,因为另一个SO答案没有解决方案,但问题的一个注释有一个解决方案,我想接受,因为它适用于该场景 原始问题 我很难用非抽象基类和选择适当扩展方法的子类编写扩展方法 下面我有一个非常简单的例子(从一个更大的项目中抽象出来),它使用扩展方法“Run”。预期输出列在每个类旁边的注释中 public class Parent { }; // Should output "Parent" public class ChildA : Parent { }; // Should output
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())));
}
}
公共类父类{};//应输出“父级”
公共类ChildA:Parent{};//应该输出“子A”
公共类ChildB:父{};//应输出“父级”
//预期输出:ChildA、Parent、Parent
公共课程
{
公共静态void Main()
{
var commands=new List(){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;
}
}
公共静态类扩展
{
公共静态字符串运行(此ChildA模型)
{
返回“ChildA”;
}
公共静态字符串运行(此父模型)
{
return model.Run1();//更改以测试不同的方法
}
公共静态字符串Run1(此父模型)//无类型检查
{
返回“家长”;
}
公共静态字符串Run2(此父模型)//显式检查子类型
{
if(型号为ChildA)
return((ChildA)model.Run();
其他的
返回“家长”;
}
公共静态字符串Run3(此父模型)//尝试动态类型转换
{
if(model.GetType().BaseType==typeof(父级))
{
动态changedObj=Convert.ChangeType(model,model.GetType());
返回changedObj.Run();
}
其他的
返回“家长”;
}
公共静态字符串Run4(此父模型)//已尝试反射泛型类型转换
{
if(model.GetType().BaseType==typeof(父级))
{
var方法=typeof(Extensions).GetMethod(“Cast”);
var generic=method.MakeGenericMethod(新[]{model.GetType()});
//var generic=generic.Invoke(新对象(),null);
//返回generic.Run();
返回“尚未工作”;
}
其他的
返回“家长”;
}
公共静态T转换(此对象输入)
{
返回(T)输入;
}
}
为Parent
和ChildA
创建两个扩展方法,您可以使用dynamic
将关联移动到运行时
Console.WriteLine(string.Join(", ", commands.Select(c => Extensions.Run(c as dynamic))));
最佳运行方法重载是在编译时解决的,对于
List
项,它是Run(此父模型)
。多态行为可以通过扩展方法中的反射来模拟
//用法
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))));
}
}
公共类父类{};//应输出“父级”
公共类ChildA:Parent{};//应该输出“子A”
公共类ChildB:父{};//应该输出“尚未工作”
公共类ChildC:Parent{};
公共课程
{
公共静态void Main()
{
var commands=new List(){new ChildA(),new ChildB(),new Parent(),new ChildC(),(ChildA)null};
Console.WriteLine(string.Join(“,”,commands.Select(c=>c.Run());
//可以为null调用扩展方法
WriteLine(((ChildA)null.Run());
////在(ChildA)null上崩溃,出现错误:
////以下方法或属性之间的调用不明确:“Extensions.Run(ChildA)”和“Extensions.Run(ChildC)”
//WriteLine(string.Join(“,”,commands.Select(c=>Extensions.Run(c为动态));
}
}
恢复
扩展方法可以作为公共方法(.Run()
)调用,而不是作为静态的扩展。Run
扩展方法Run(此父模型)
存在null
参数问题(无法解析正确的类型)
动态技巧在大多数情况下都有效,但:
int-Run(此ChildC模型)
方法,该方法返回int
而不是字符串
(当从列表中删除(ChildA)null
时)以下方法或属性之间的调用不明确:'Extensions.Run(ChildA)'和'Extensions.Run(ChildC)
错误(我不理解)听起来你要找的是多态性,扩展方法上的多态性就是这个。。。谢谢你找到了一个基本相同的老问题。我喜欢上面提到的访问者模式,因为应用程序正在做什么,任何将扩展更改为实体模式的opp都是好的。谢谢你的评论!使用反射方法的方法可以工作:@ASh-这种方法非常有效。我指定重新打开这个问题,以便您可以根据需要将其作为答案。似乎更简单的方法是
Console.WriteLine(string.Join(“,”,commands.Select(c=>Extensions.Run(c as dynamic))
摘自@Damien\u的链接,不信者添加了一个带有动态
陷阱的答案。请读
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))));
}
}