2008-09-26 99 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); 
}