【问题标题】:C# generic method for multiple classes多个类的 C# 泛型方法
【发布时间】:2014-10-29 23:10:43
【问题描述】:

我试图寻找解决方案,但我的问题是我什至不知道该使用什么术语。泛型、委托、LINQ、反射和抽象想法可能是解决方案的一部分,但我的“Google-fu”并没有找到正确的答案。

问题: 我有多个类(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 中的每一个都会实现一些通用类,我可以使用它,但我不能。每个类都是独立的,但具有相同的命名属性。

关于概念/代码的任何指导?

谢谢!

【问题讨论】:

  • 听起来课程是给定的,他不能改变它们
  • 我将添加一些说明。这些类是从 XSD.exe 动态生成的。我正在尝试使用大量 XSD 来填充兼容的 XML 文档。使用 XSD.exe 将所有内容都变成了我可以轻松引用的类。我想这样做,所以如果我必须重新运行 XSD.exe(对于新版本),我不必返回并重新编辑(使用接口)从 XSE.exe 动态创建的文件

标签: c# .net linq generics reflection


【解决方案1】:

为您的属性创建一个interface

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()

【讨论】:

  • 卢卡斯,谢谢,但是不符合我上述要求的部分是 ClassA : IDoThis 部分。问题是有许多这些类是由 XSD.EXE 自动创建的,我必须手动更改所有这些类。虽然这是你做这类事情的方式,但这不是我在上面寻找的。不过谢谢!
  • 顺便说一句,我不想​​冒犯任何人,但我可以否决这个,因为这不是我想要的吗?也就是说,我知道这个解决方案,但正在寻找一种不使用接口的方法。
  • @Adam 好的,没问题,我以为你可以使用这个解决方案。至于downvote,你还不能downvote,因为你没有足够的声誉,但你可以将最有帮助的答案标记为已接受,它将首先突出显示。另一点是:您通常应该只对有问题的答案或您不同意的答案投反对票,而不是正确的答案(希望我的答案是正确的)但您没有/无法使用。但最终投票取决于你,所以如果你真的觉得你应该投反对票,等你有足够的声望后再回来。
  • 感谢您的指导!我一直在使用该站点来获取想法,但从未真正投入使用它。我不会投反对票。
【解决方案2】:

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

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);

我没有检查过这个 - 所以语法可能有点不对 - 但这是反射方法调用的基本机制。如果有多个具有相同名称的方法,如果方法带有参数等,你需要变得更高级

【讨论】:

  • 如果别无选择,我只会诉诸后期绑定。如果您自己设计这些类并且可以选择实现一个通用接口,那么这将是一个更可取的解决方案。
  • 我同意 - 我对最后一段的阅读是课程是给定的 - 他无法更改它们
  • 这个是我觉得最合适的一个,顺便说一下哪个c#版本需要编译动态?
  • 谢谢!我在搜索中没有遇到“动态”。我很好奇纯反射解决方案会是什么样子。谷歌我去......顺便说一句,Visual C# 2010 包括动态:msdn.microsoft.com/en-us/library/dd264736.aspx
  • +1 for dynamic,无论如何它比反射更容易,更易读。
【解决方案3】:

至少有四个选项可供您选择 - 可能更多。

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

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

如果你问我更喜欢这两者中的哪一个,我想我会选择 3。超过 4。但这是个人偏好。

【讨论】:

  • 选项的精彩总结。虽然 1 和 2 并非无法实现(我确实可以访问代码),但识别和重新编码所有类需要做一些工作。如果 3 和 4 不是很好的解决方案,仍然有可能。我很好奇反射会是什么样子。
  • @LucasTrzesniewski:我说'至少',不是吗... :-))
  • @Adam:今天或明天有时间我会详细说明。
  • @Axel true... 但我还是将此方法添加为另一个答案,因为它丢失了;)
  • @Axel 反射是否类似于(假设 mClassA 是从 A 类实例化的):var obj = mClassA.GetType(); PropertyInfo aProp = obj.GetProperty("DoThisA"); PropertyInfo bProb = obj.GetProperty("DoThisB"); aProp.SetValue(obj, "Foo"); bProp.SetValue(obj, "42"); 这看起来是否正确?
【解决方案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;
    }
}

【讨论】:

  • 基里尔,请查看我上面的 cmets 以获得 Lucas 的回复。这是简单的方法,但不是我想要按照我的要求做的。
【解决方案5】:

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

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,因为它更容易。

【讨论】:

    猜你喜欢
    • 2011-12-25
    • 1970-01-01
    • 2015-12-28
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 1970-01-01
    • 2020-05-03
    • 2010-12-26
    相关资源
    最近更新 更多