【问题标题】:How can I extend a field of a class?如何扩展类的字段?
【发布时间】:2013-02-21 13:00:53
【问题描述】:

假设我有一个 Person 列表(这是一个类)。它包含大约 20 个字段(姓名、姓氏、年龄、出生日期等)。所以我得到了这个列表:

var listOfPersons= MyContext.Persons.Cast<Person>();

现在,我需要遍历这个列表,并为每个 Person 添加一个新字段(它在类中不存在),比如说,CurrentDateTime

我可以使用新字段创建一个新对象,并将值从 Person 复制并粘贴到新类。比如:

PersonNew newPerson = new PersonNew("Name", "Surname", "Age", "DateOfBirthdate", ... "CurrentDateTime");

但是,如果将来我更改 Person 类,这将非常糟糕。那么,有没有一种策略可以用一个新的领域来“扩展人”呢?这需要 Person 实例(无论它是什么)并添加新字段?

【问题讨论】:

  • 你想使用这个扩展的Person多久?如果只是为了这个方法,你可以使用匿名类型。
  • 你必须在哪种情况下这样做?
  • 在不修改扩展类的情况下,任何解决方案都无法避免对基类进行任意更改。这是“脆弱的基类问题”。
  • 是的,我应该使用匿名类型...但是如何?无法在 LINQ 中“扩展”它
  • 不可思议的是没有办法……它只是一个对象的复制到另一个“更多”大哈哈

标签: c# .net linq extend


【解决方案1】:

首先,我建议使用扩展方法来投影集合而不是迭代。像这样:

var newCollection = oldCollection.Select(entity => MakeNewType(entity))

其次,您所说的“用新字段扩展人”是什么意思并不完全清楚。以下是您可以实现这一目标的几种方法。

1) 使用新字段创建另一个类并将其映射到旧字段。这是 asp.net mvc 应用程序的常见场景,您将模型映射到适当的视图模型。 Automapper 对于这些类型的场景很有用(参见 Sławomir Rosiek anwser)

2) 在 c# 4+ 中利用 dlr。 Yuo 将失去对动态对象的智能感知,但它们可以在函数中传递

var newPeople = people.Select(p =>
    {
        dynamic expando = new ExpandoObject();
        expando.Id = p.Id;
        expando.FirtName = p.FirtName;
        /* ... */
        expando.CurrentDateTime = DateTime.Now;
        return expando;
    });

3) 使用匿名类型。匿名类型不能传递给其他函数,因此当您需要在单个方法中快速投影数据并计算一些结果时,这种方法很有用

var newPeople = people.Select(p => new 
    { 
        Id = p.Id, 
        FirtName = p.FirtName, 
        /* ... */ 
        CurrentDateTime = DateTime.Now 
    });

在这两种情况下,您现在都可以访问新“创建”的属性:

foreach(var p in newPeople)
{
    Console.WriteLine("CurrentDateTime: {0}", p.CurrentDateTime);
}

4) 如果您确实需要在运行时创建一个功能齐全的 .net 类,您可以使用Reflection.Emit。此场景通常用于创建动态代理 - 实现某些仅在运行时才知道的功能的子类。实体框架执行此操作。

【讨论】:

    【解决方案2】:

    一种解决方案是将您的班级设为partial,并将您的字段添加到班级的另一个partial 定义中:

    public partial class Person
    {
        public string Name { get; set; }
        public string FirstName { get; set; }
        ...
    }
    
    ...
    
    public partial class Person
    {
        public DateTime CurrentDateTime { get; set; }
    }
    
    ...
    
    var listOfPersons = MyContext.Persons.Cast<Person>();
    foreach (var person in listOfPersons)
    {
        person.CurrentDateTime = ....
    }
    

    请注意,您将使用类的相同实例。

    【讨论】:

    • 我不能让我的班级部分上课。附带指定的 DLL
    【解决方案3】:

    我认为您描述的解决方案效果很好。如果您想在不扩展 Person 类的情况下跟踪每个人的生日,您可以使用 Dictionary 对象

    var listOfPersons = MyContext.Perons.Cast<Person>();
    Dictionary<Person, DateTime> birthdays = new Dictionary<Person, DateTime>    
    foreach(Person person in listOfPersons)
    {
        birthdays.Add(person, getBirthday(person); 
    }
    

    【讨论】:

    • @paizza 好吧,答案的重点是您不需要这样做。即使在技术上没有回答问题,它也可以解决您的问题。
    【解决方案4】:

    您可以创建一些静态方法,使用 Automapper 从 Person 创建 PersonNew

    public class PersonNew : Person
    {
        public static PersonNew CreateFromPerson(Person person, DateTime currentDateTime)
        {
            var newPerson = Mapper.Map<PersonNew>(person);
            newPerson.CurrentDateTime = currentDateTime;
        }
    }
    

    【讨论】:

    • 在这个例子中只支持对基类的某些类型的更改是不值得的;复杂的添加/修改可能会导致此解决方案无法正常工作。
    • @paizza 如果存在基于时间的内部属性,例如存储对象的创建日期,或者它在创建时捕获了某些其他类型的外部资源的状态(例如同步上下文)。任何未存储在公共属性/字段中的信息,例如通过方法设置的信息。当然,因为您有一个新对象,所以对旧对象的任何现有引用都不会引用新对象,这对您来说可能并不理想。真的,如果您的对象不仅仅是拥有一堆属性,那么它很可能不起作用。
    猜你喜欢
    • 1970-01-01
    • 2016-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-17
    相关资源
    最近更新 更多