C# 具有基类和子类的扩展方法

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

更新

请求重新打开,因为另一个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())));
    }
}
公共类父类{};//应输出“父级”
公共类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());
}
}
以下是我迄今为止的尝试,但必须有一种更干净的方法来做到这一点:

  • 无类型检查-导致父扩展方法被独占使用(父、父、父)
  • 显式类型检查-右输出,但必须显式检查每个扩展可能性的类型(ChildA、Parent、Parent)
  • 尝试将.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;   
        }
    
    }
    
    公共静态类扩展
    {
    公共静态字符串运行(此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))));
        }
    }