2008-09-26 50 views
1

我正在试验泛型,我试图创建类似于数据集类的结构。
我有以下代码类与valueTypes字段和拳击

public struct Column<T> 
{ 
    T value; 
    T originalValue; 

    public bool HasChanges 
    { 
     get { return !value.Equals(originalValue); } 
    } 

    public void AcceptChanges() 
    { 
     originalValue = value; 
    } 
} 

public class Record 
{ 
    Column<int> id; 
    Column<string> name; 
    Column<DateTime?> someDate; 
    Column<int?> someInt; 

    public bool HasChanges 
    { 
     get 
     { 
      return id.HasChanges | name.HasChanges | someDate.HasChanges | someInt.HasChanges; 
     } 
    } 

    public void AcceptChanges() 
    { 
     id.AcceptChanges(); 
     name.AcceptChanges(); 
     someDate.AcceptChanges(); 
     someInt.AcceptChanges(); 
    } 
} 

问题我已经是,当我添加新的专栏中,我还需要添加它在HasChanges属性和的AcceptChanges()方法。这只是要求重构。
是卡梅斯我的脑海所以第一个解决方案是这样的:

public interface IColumn 
{ 
    bool HasChanges { get; } 
    void AcceptChanges(); 
} 

public struct Column<T> : IColumn {...} 
public class Record 
{ 
    Column<int> id; 
    Column<string> name; 
    Column<DateTime?> someDate; 
    Column<int?> someInt; 

    IColumn[] Columns { get { return new IColumn[] {id, name, someDate, someInt}; }} 

    public bool HasChanges 
    { 
     get 
     { 
      bool has = false; 
      IColumn[] columns = Columns;   //clone and boxing 
      for (int i = 0; i < columns.Length; i++) 
       has |= columns[i].HasChanges; 
      return has; 
     } 
    } 

    public void AcceptChanges() 
    { 
     IColumn[] columns = Columns;   //clone and boxing 
     for (int i = 0; i < columns.Length; i++) 
      columns[i].AcceptChanges();   //Here we are changing clone 
    } 
} 

你可以从注释中看到我们这里有几个问题与结构克隆。对此的简单解决方案是将Column更改为类,但是从我的测试看来,它增加了40%的内存使用量(因为每个对象元数据),这是我无法接受的。

所以我的问题是:有没有人有任何其他的想法如何创建方法,可以在不同的结构化对象/记录工作?也许F#社区的某个人可以提出功能语言如何解决这些问题,以及它如何影响性能和内存使用。

编辑:
SFG谢谢你关于宏的建议。
在Visual Studio 2008中,有内置(但不是众所周知的)称为T4的模板引擎。总的来说,我要将'.tt'文件添加到我的项目中,并创建一个模板来搜索我所有的类,以某种方式识别记录(例如通过它们实现的某个接口),并用HasChanges和AcceptChanges生成部分类)将只调用该类包含的列。

一些有用的链接:
T4 Editor for VS
Blog with links and tutorials about T4
Blog entry with example that uses EnvDTE to read project files

回答

1

正如你问功能语言的例子;在lisp中,您可以通过使用宏来为您启动代码,从而避免在每次添加列时编写所有代码。可悲的是,我认为这在C#中是不可能的。

在性能方面:宏将在编译时进行评估(因此编译速度很慢),但在运行时不会导致速度下降,因为运行时代码与您的会手动写入。

我想你可能不得不接受你最初的AcceptChanges(),因为如果你想避免写入克隆版本,你需要直接通过它们的标识符访问结构。

换句话说:您需要一个程序来为您编写程序,而且我不知道如何在C#中完成这些工作,而不需要通过将结构切换到类来达到特殊长度或损失更多性能(如反思)。

0

你可以使用反射来遍历成员,并调用HasChanges和AcceptChanges的。 Record类可以将反射元数据存储为静态,因此不存在每个实例的内存开销。但是,运行时的性能成本将会非常高 - 您最终还可能会对列进行装箱和拆箱,从而增加成本。

0

说实话,这听起来像是你真的希望这些Column s是类,但不想支付与类关联的运行时成本,所以你试图让它们成为结构。我不认为你会找到一个优雅的方式去做你想做的事。结构被认为是值类型,你想让它们像引用类型一样。

您不能有效地将您的列存储在IColumn的数组中,因此没有任何数组方法可以正常工作。编译器无法知道IColumn数组只能保存结构,事实上,如果这样做,它也无济于事,因为仍然存在不同类型的结构。每当有人打电话给AcceptChanges()HasChanges()时,你最终会结束并克隆你的结构,所以我非常怀疑,让你的Column成为一个结构而不是类会节省你很多的内存。

但是,您可能可能会将您的Column s直接存储在数组中,并使用枚举为它们编制索引。例如:

public class Record 
{ 
    public enum ColumnNames { ID = 0, Name, Date, Int, NumCols }; 

    private IColumn [] columns; 

    public Record() 
    { 
     columns = new IColumn[ColumnNames.NumCols]; 
     columns[ID] = ... 
    } 

    public bool HasChanges 
    { 
     get 
     { 
      bool has = false; 
      for (int i = 0; i < columns.Length; i++) 
       has |= columns[i].HasChanges; 
      return has; 
     } 
    } 

    public void AcceptChanges() 
    { 
     for (int i = 0; i < columns.Length; i++) 
      columns[i].AcceptChanges(); 
    } 
} 

我没有一个C#编译器方便,所以我不能检查,看看是否会工作或没有,但基本的思路应该工作,即使我没有得到所有细节正确。不过,我只需继续并让他们上课。无论如何你都在为此付出代价。

0

我能想到做你真正想做的唯一方法就是使用反射。这仍然是box/unbox,但它允许您将克隆存储回现场,有效地使其成为真正的价值。

public void AcceptChanges() 
{ 
    foreach (FieldInfo field in GetType().GetFields()) { 
     if (!typeof(IColumn).IsAssignableFrom(field.FieldType)) 
      continue; // ignore all non-IColumn fields 
     IColumn col = (IColumn)field.GetValue(this); // Boxes storage -> clone 
     col.AcceptChanges(); // Works on clone 
     field.SetValue(this, col); // Unboxes clone -> storage 
    } 
} 
0

如何:

public interface IColumn<T> 
{ 
    T Value { get; set; } 
    T OriginalValue { get; set; } 
} 

public struct Column<T> : IColumn<T> 
{ 
    public T Value { get; set; } 
    public T OriginalValue { get; set; } 
} 

public static class ColumnService 
{ 
    public static bool HasChanges<T, S>(T column) where T : IColumn<S> 
    { 
     return !(column.Value.Equals(column.OriginalValue)); 
    } 

    public static void AcceptChanges<T, S>(T column) where T : IColumn<S> 
    { 
     column.Value = column.OriginalValue; 
    } 
} 

客户端代码则是:

Column<int> age = new Column<int>(); 
age.Value = 35; 
age.OriginalValue = 34; 

if (ColumnService.HasChanges<Column<int>, int>(age)) 
{ 
    ColumnService.AcceptChanges<Column<int>, int>(age); 
}