如何在c#中克隆类型T

本文关键字:类型 | 更新日期: 2023-09-27 18:15:00

我有一个类型Foo如下:

[Alias("Boo")]
public class Foo
{
  public int Id { get; set; }
  public string Name { get; set; }
}

我正在保存对象属性更改的历史记录,出于性能原因,我想创建第二个表来推送所有历史记录。不幸的是,OrmLite不支持每种类型的多个表。在定义时,将简单地使用别名来代替类型名称。

由于我使用通用数据控制器,我需要克隆实际类型 T(具有不同的名称Boo)。我不需要在两种类型之间转换/强制转换对象,但如果可能的话,这将是有帮助的。

如何在c#中克隆类型T

"克隆"一个实例是相当模糊的:它是深克隆还是浅克隆?如果很深,有多深?是否包括其他可能的poco ?如果是这样,它将如何包括他们?使用id?使用副本?

另外,在不同的表中存储对象的完整副本并不是实现历史记录的方法。

如果我是你,我会有一个不同的类型来存储序列化的时间变化,然后在你的实体中有一个变化列表。

这就是我在最近的一些项目的poco中实现它的方式(使用实体框架进行持久化,根据需要适应OrmLite…也删除了一些东西):

/* BaseEntity just adds "Id" and some other properties common to all my
   POCOs */
public class EntityHistory : BaseEntity
{
  /* ... Other properties removed for clarity ... */
  public Guid TransactionId { get; set; }
  public DateTime TransactionTime { get; set; }
  public string OldValues { get; set; }
  public string NewValues { get; set; }
}

然后有一个基本实体(或接口,我使用一个基本实体,因为我不需要额外的多重继承,并且节省了我在所有类中编写相同的属性)为那些需要存储历史的实体:

public abstract class BaseTrackedEntity : BaseEntity, IAuditableEntity, IChangeTrackedEntity
{
  /* ... Other properties removed for clarity ... */
  public ICollection<EntityHistory> Histories { get; set; }
}

那么你的实体将从BaseTrackedEntity继承。

在对数据库进行持久化更改时,您会发现更改(在EF中很容易,它给您一个ChangeSet,不知道OrmLite,但如果它不提供方法,您可以使用反射来解决),从加载的实体到保存的实体,并将它们序列化到EntityHistory实例(在OldValuesNewValues中)并将其添加到Histories列表中。我用JSON,你也可以用其他格式

这适用于日志记录目的(我没有复制我的日志记录属性-如User, CreationTime,和东西-但他们会在BaseTrackedEntity)和Undoing (TransactionIdEntityHistory组撤销阶段,如果需要的话),它的性能相当高(比克隆和存储整个对象每次更新一次),它很容易实现和维护。

能否添加并实现IClonable<T> ?

interface ICloneable<T>
{
  T Clone();
}
[Alias("Boo")]
public class Foo : ICloneable<Foo>
{
  public int Id { get; set; }
  public string Name { get; set; }
  public Foo Clone() { ... }
}

您可以使用继承来克隆定义

解决方案比我想象的要简单——尽管一开始我并没有想到:继承!

因为我无法控制Foo类,所以我不能手动编写一个新的类型来反映它(Foo中的成员可能会更改)。创建一个继承自Foo的新类型Boo,没有定义额外的成员,将允许使用OrmLite使用新名称进行类型转换和创建表。

public class Foo
{
  public int Id { get; set; }
  public string Name { get; set; }
}
public class Boo : Foo
{
}