簡體   English   中英

具有valueTypes字段和拳擊的類

[英]class with valueTypes fields and boxing

我正在嘗試泛型,並且試圖創建類似於Dataset類的結構。
我有以下代碼

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更改為class,但是從我的測試看來,它使內存使用量增加了約40%(由於每個對象元數據),這對我來說是不可接受的。

所以我的問題是:是否有人對如何創建可在不同結構化對象/記錄上使用的方法有其他想法? 也許來自F#社區的人可以建議如何使用功能語言解決此類問題,以及如何影響性能和內存使用。

編輯:
sfg感謝您對宏的建議。
在Visual Studio 2008中,有一個稱為T4的內置(但未知)模板引擎。 整個過程就是將“ .tt”文件添加到我的項目中,並創建一個模板來搜索我的所有類,以某種方式識別記錄(例如通過它們實現的某些接口),並使用HasChanges和AcceptChanges( ),將僅調用該類包含的列。

一些有用的鏈接:
VS的T4編輯器
有關T4的鏈接和教程的博客
帶有使用EnvDTE讀取項目文件的示例的博客條目

如您所要求的功能語言示例; 簡而言之,您可以使用宏為您修改代碼,從而避免在每次添加列時編寫所有代碼。 可悲的是,我認為這在C#中是不可能的。

就性能而言:宏將在編譯時進行評估(因此會使編譯速度變慢),但不會在運行時導致運行速度降低,因為運行時代碼與您手動編寫的代碼相同。

我認為您可能必須接受原始的AcceptChanges(),因為如果要避免寫入克隆的版本,則需要直接通過其標識符訪問結構。

換句話說:您需要一個程序來為您編寫該程序,而且我不知道如何在C#中做到這一點,而又不會花費過多的時間或通過將結構切換到類(例如反射)而比以前損失更多的性能。

您可以使用反射來遍歷成員並調用HasChanges和AcceptChanges。 Record類可以將反射元數據存儲為靜態,因此不存在每個實例的內存開銷。 但是,運行時的性能成本將是巨大的-您可能最終還會對列進行裝箱和拆箱,這會增加更多的成本。

老實說,聽起來您確實希望這些Column成為類,但是不想支付與類相關的運行時成本,因此您正在嘗試使它們成為結構。 我認為您不會找到一種優雅的方式來做自己想要的事情。 結構應該是值類型,您想使它們的行為類似於引用類型。

您無法將Columns有效地存儲在IColumn的數組中,因此沒有一種數組方法可以很好地工作。 編譯器無法知道IColumn數組僅保留結構,並且確實做到了,也無濟於事,因為您仍然嘗試在其中IColumn不同類型的結構。 每當有人調用AcceptChanges()HasChanges() ,無論如何都要結束裝箱並克隆結構,因此我嚴重懷疑使Column成為結構而不是類會節省大量內存。

但是,您可能可以Column直接存儲在數組中,並使用枚舉對其進行索引。 例如:

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#編譯器,因此無法檢查它是否行得通,但是即使我沒有正確理解所有細節,但基本思想還是行得通。 但是,我只是繼續讓他們上課。 您還是要為此付費。

我能想到要做的唯一方法就是使用Reflection。 仍然可以裝箱/拆箱,但是可以讓您將克隆存儲回字段中,從而有效地使它成為真正的價值。

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
    }
}

這個怎么樣:

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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM