用于多个类的C#泛型方法

本文关键字:泛型方法 用于 | 更新日期: 2023-09-27 18:28:17

我试图寻找解决方案,但我的问题是我甚至不知道该使用什么术语。泛型、委托、LINQ、反射和抽象思想可能是解决方案的一部分,但我的";谷歌傅"没有找到正确的答案。

问题:我有多个类(ClassAClassBClassC),它们都具有相同的2-3属性DoThisADoThisBDoThisC

代码的工作方式是,当我处理每个类时,我总是希望执行相同的代码来设置DoThisADoThisBDoThisC

例如,为了简化,逻辑将始终是:

{some computations to set string currentValueImProcessing to something}
if (xyz) [ClassA|B|C].DoThisA = currentValueImProcessing
else [ClassA|B|C].DoThisB = currentValueImProcessing

我不想一遍又一遍地写同样的语句,那么我如何将对类(a,B,C)的引用发送到一个方法来执行逻辑呢?

如果写得正确,ClassAClassBClassC中的每一个都会实现一些泛型类,我可以使用它,但我不能。每个类都是独立的,但具有相同的命名属性。

有关于概念/代码的指导吗?

谢谢!

用于多个类的C#泛型方法

为您的属性创建一个接口:

internal interface IDoThis
{
    public string DoThisA { get; set; }
    public string DoThisB { get; set; }
    public string DoThisC { get; set; }
}

然后,让你的类实现它:

public class ClassA : IDoThis
{
    public string DoThisA { get; set; }
    public string DoThisB { get; set; }
    public string DoThisC { get; set; }
}
public class ClassB : IDoThis
{
    // Same properties
}
public class ClassC : IDoThis
{
    // Same properties
}

这样,你就可以在某个地方创建一个静态初始化器方法:

internal static class MyClassesExtensions
{
    public static void InitTheStuff(this IDoThis obj)
    {
        // Do something here, for example:
        if (String.IsNullOrEmpty(obj.DoThisA))
            obj.DoThisA = "foo";
        else
            obj.DoThisB = obj.DoThisC;
    }
}

然后您可以在ClassAClassBClassC中的任意位置调用this.InitTheStuff()

您可以使用反射,也可以使用动态(动态将为您使用反射)

dynamic obj = new ClassA();
obj.DoTHisA();

是如何使用动态

我假设您谈论的是要实例化的类。如果DoThisA、B、C是静态方法,则必须使用反射

注意-如果您可以更改类,那么按照其他人的建议添加一个接口,甚至添加一个通用基类

反射像这个

var type = obj.GetType(); // obj is ClassX object
var method = type.GetMethod("DoTHisA");
method.Invoke(obj);

我还没有检查过这一点,所以语法可能有点错误,但这是反射方法调用的基本机制。如果有多个方法具有相同的名称,如果这些方法带有参数等

至少有四个选项可供选择,甚至更多。

  1. 创建一个接口,该接口由您的所有类实现,并包含通用方法
  2. 创建一个基类,所有类都从中继承。然后可以在基类中实现通用功能。如果实现因类而异,但您可以为方法定义公共签名,则使基类成为公共函数抽象。然后,您可以在每个类中实现实际的功能
  3. 在@pm100的解决方案中使用动态对象
  4. 使用反射访问通用功能

作为指导方法1。和2。是首选,因为它们允许在编译时检查代码。但是,如果您无法控制包含通用功能的类,例如,您无权访问源代码,或者您被允许对代码进行更改,则可以使用其他两种方法。

如果你问我更喜欢这两种,我想我会选3种。超过4。但这是个人偏好。

您谈论的是继承问题。

对于您的任务,您需要一个具有通用属性的抽象基类:

public abstract class Base
{
    public bool DoThisA { get; set; }
    public bool DoThisB { get; set; }
}

和子类:

public class A : Base { }
public class B : Base { }
public class C : Base { }

之后,您可以创建一个方法,该方法将接受类型为Base的对象

public void Do(Base b, bool xyz, bool currentValueImProcessing)
{
    if (xyz)
    {
        b.DoThisA  = currentValueImProcessing;
    }
    else
    {
        b.DoThisB  = currentValueImProcessing;
    }
}

这里已经提供了很多方法,所以为了完整性。。。以下是一些运行时代码生成:

public class ClassA
{
    public string DoThisA { get; set; }
    public int DoThisB { get; set; }
    public bool DoThisC { get; set; }
    public void Init()
    {
        // You can call this from anywhere, even from an unrelated class
        MyClassInitializer<ClassA>.Init(this);
    }
}
public static class MyClassInitializer<T>
{
    // Create the getters/setters you need, and make sure they're static.
    private static readonly Func<T, string> _getA = BuildGetter<string>("DoThisA");
    private static readonly Action<T, string> _setA = BuildSetter<string>("DoThisA");
    private static readonly Func<T, int> _getB = BuildGetter<int>("DoThisB");
    private static readonly Action<T, int> _setB = BuildSetter<int>("DoThisB");
    private static readonly Func<T, bool> _getC = BuildGetter<bool>("DoThisC");
    private static readonly Action<T, bool> _setC = BuildSetter<bool>("DoThisC");
    private static Func<T, TValue> BuildGetter<TValue>(string name)
    {
        var obj = Expression.Parameter(typeof(T));
        return Expression.Lambda<Func<T, TValue>>(Expression.Property(obj, name), obj).Compile();
    }
    private static Action<T, TValue> BuildSetter<TValue>(string name)
    {
        var obj = Expression.Parameter(typeof(T));
        var value = Expression.Parameter(typeof(TValue));
        return Expression.Lambda<Action<T, TValue>>(Expression.Assign(Expression.Property(obj, name), value), obj, value).Compile();
    }
    public static void Init(T obj)
    {
        // Here's your custom initialization method
        if (_getA(obj) == "Foo")
            _setB(obj, 42);
        else
            _setC(obj, true);
    }
}

不一定是最容易掌握的,但这应该比使用dynamic或反射快得多。也就是说,如果你不需要速度,坚持使用dynamic,因为它更容易。