【问题标题】:Method to handle objects with properties in common, but different object types处理具有共同属性但对象类型不同的对象的方法
【发布时间】:2013-08-14 06:36:07
【问题描述】:

我有大量自动生成的对象。尽管它们都是不同的、不相关的类,但所有对象都共享一些基本属性(名称、id 等)。 我不控制这些对象的生成,所以很遗憾,我无法采用理想的方法来实现接口。我想创建一个方法,在该方法中我可以传递这些对象中的任意一个并执行某些操作使用这些通用属性。

一般的想法是这样的:

someObj a = new someObj();
a.name = "sara";
diffObj b = new diffObj();
b.name = "joe";
string phrase = string.Format("I am with {0} and {1}", 
    getName(a), getName(b));

private string getName(object anyObjWithName)
{
    return anyObjWithName.name;
}

虽然这自然是行不通的。

我认为一个泛型方法可能有答案,但我能看到用当前类型调用它的唯一方法是使用 genericMethod.Invoke ,它仍然存在无法解析传递的属性的相同问题方法中的对象。这与 Calling generic method with a type argument known only at execution timeHow to call generic method with a given Type object? 不同,后者在方法中仅使用类型或类型的属性,而不是对象的属性。

我知道这会(非常)容易出错,但我可以保证遇到的所有对象都将具有被操作的公共属性。

【问题讨论】:

  • 尝试声明一个接口来关联对象的公共属性,或者使用反射。
  • 啊,也许我需要更清楚一点。由于我的对象的生成方式,我无法使用良好的 OOP 实践并实现接口。不幸的是,我无法控制这个过程。

标签: c# asp.net


【解决方案1】:

我可以保证遇到的所有对象都具有被操纵的共同属性

如果是这样,你可以使用dynamic

private string getName(dynamic anyObjWithName)
{
    return anyObjWithName.name;
}

请注意,使用任何没有name 属性的对象在运行前都不会失败。

如果你想增加一点安全性,你可以捕捉RuntimeBinderException,如果该属性不存在则抛出:

private string getName(dynamic anyObjWithName)
{
    try {
        return anyObjWithName.name;
    }
    catch(RuntimeBinderException) {
        return "{unknown}";
    }
}

【讨论】:

  • 对于我的 $.02,我认为 OP 无法开始 dynamic 路径。这条路充满了危险,这个答案根本不是教学法。
  • 我知道我会为此感到痛苦。由于我无法实现接口,我不确定是否还有其他选择。
  • 请用粗体大字编辑您的问题,否则许多其他人可能会在界面可用时使用动态解决方案。
  • @MaxPRafferty 据我所知,这仅来自 .net 4.0 及更高版本。
  • @KeithPayne 更新了!感谢您的提醒。
【解决方案2】:

如果您对 D Stanley 提到的使用动态的性能不满意,您可以随时尝试FastMember

开始使用它所需要知道的一切都在前 2 个代码示例中显示。

【讨论】:

  • 哇,是的,这个库的存在就是为了解决这个确切的问题。我想知道它是如何在没有反射的情况下在引擎盖下做到的......
  • @MaxPRafferty:IIRC,它将利用反射第一次获取成员信息,编写等效的编译时 IL,然后使用该编译代码进行后续访问该类型/成员。与频繁命中 DLR 或反射相比,这使其速度更接近于已编译的代码。
【解决方案3】:

您正在那里创建一个 Rube Goldberg 设备。您应该让所有数据对象类都实现一个接口,然后您就可以使用它了。比摆弄反射更简单,更不容易出错。

许多对象具有共同的属性但不共享相同的祖先,至少在一个共同的界面上,这一事实表明您的设计有问题。请重新考虑。

【讨论】:

  • 这才是真正的问题所在——我不控制对象的生成。我的王国的界面!
【解决方案4】:

有多种方法可以做到这一点,最简单的可能是创建接口并在那里声明公共方法,让你的对象实现它,然后将“getName”方法更改为接口对象

private string getName(IMyInterface anyObjWithName)
{
    return anyObjWithName.name;
}

【讨论】:

    【解决方案5】:

    正确的方法是使用接口,如果您拥有正在使用的类型

    public interface IEntity
    {
        int ID { get; set; }
        string Name { get; set; }
    }
    
    public class TypeOne : IEntity
    {
        public int ID { get; set; }
        public string Name { get; set }
    
        public string BespokePropertyOne { get; set;}
    }
    
    public class TypeTwo : IEntity
    {
        public int ID { get; set; }
        public string Name { get; set; }
    
        public float BespokePropertyTwo { get; set; }
    }
    
    static void Main(string[] args)
    {
        List<IEntity> entities = new List<IEntity>();
        entities.Add(new TypeOne() { ID = 1, Name = "Bob", BespokePropertyOne = "blablabla" });
        entities.Add(new TypeTwo() { ID = 2, Name = "Alice", BespokePropertyTwo = 5.4f });
    
        foreach (IEntity entity in entities)
        {
            Console.WriteLine("ID: {0} Name: {1}", entity.ID, entity.Name);
        }
    }
    

    【讨论】:

    • OP 没有办法控制类。它在问题中提到。
    【解决方案6】:

    这个答案是在编辑问题之前写的,说明在这种情况下接口是不可能的。也许它可以帮助其他人阅读这个问题。

    界面:

    interface Iname
    {
        string Name { get; set; }
    }
    

    使用界面:

    class A : Iname
    {
        public string Name { get; set; }
    }
    
    class B : Iname
    {
        public string Name { get; set; }
    }
    

    方法:

    string GetName(Iname o)
    {
        return o.Name;
    }
    

    用途:

    A a = new A { Name = "First" };
    B b = new B { Name = "Last" };
    Text = GetName(a) + " " + GetName(b);
    

    【讨论】:

    • OP 没有办法控制类。它在问题中提到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 2021-04-19
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    相关资源
    最近更新 更多