具有valueTypes字段和装箱的类

时间:2020-03-06 14:48:31  来源:igfitidea点击:

我正在尝试泛型,并且试图创建类似于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%(由于每个对象元数据),这对我来说是不可接受的。
所以我的问题是:是否有人还有其他想法如何创建可在不同结构化对象/记录上使用的方法?也许Fcommunity的某人可以建议如何使用功能语言解决此类问题,以及它如何影响性能和内存使用。
编辑:
sfg感谢我们对宏的建议。
在Visual Studio 2008中,有一个称为T4的内置(但未知)模板引擎。重点是将'.tt'文件添加到我的项目中,并创建一个模板来搜索我的所有类,以某种方式识别记录的类(例如,通过它们实现的某些接口),并使用HasChanges和AcceptChanges( ),它将仅调用该类包含的列。
一些有用的链接:
VS的T4编辑器
有关T4的链接和教程的博客

解决方案

带有使用EnvDTE读取项目文件的示例的博客条目

我们可以使用反射来遍历成员并调用HasChanges和AcceptChanges。 Record类可以将反射元数据存储为静态,因此不存在每个实例的内存开销。但是,运行时的性能代价将是巨大的-我们可能最终还会对列进行装箱和拆箱,这会增加更多成本。

如我们所要求的功能语言示例;简而言之,我们可以使用宏为我们修改代码,从而避免在每次添加列时编写所有代码。可悲的是,我认为这在C#中是不可能的。

在性能方面:宏将在编译时进行评估(因此会使编译速度变慢),但由于运行时代码与我们手动编写的代码相同,因此不会在运行时导致运行速度下降。

我认为我们可能必须接受原始的AcceptChanges(),因为如果要避免写入克隆的版本,则需要直接通过结构的标识符访问结构。

换句话说:我们需要一个程序来为我们编写该程序,而且我不知道如何在C中做到这一点,而又不会通过将结构切换到类(例如反射)而花费过多的时间或者比以前损失更多的性能。

老实说,听起来我们确实希望这些Column作为类,但不想支付与类相关的运行时成本,因此我们正在尝试使它们成为结构。我认为我们不会找到一种优雅的方式来做自己想要的事情。结构应该是值类型,并且我们想使它们的行为类似于引用类型。

我们无法将Columns有效地存储在IColumn的数组中,因此没有一种数组方法可以很好地工作。编译器无法知道" IColumn"数组仅包含结构,实际上,这样做并没有帮助,因为我们仍然尝试在其中插入不同类型的结构。每当有人调用AcceptChanges()或者HasChanges()时,无论如何,我们最终都将装箱并克隆结构,因此我严重怀疑将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();
    }
}

但是,我们可能可以将Column直接存储在数组中,并使用枚举对其进行索引。例如:

我没有Ccompiler,所以我无法检查它是否行得通,但是即使我没有正确理解所有细节,基本构想也应该行得通。但是,我只是继续让他们上课。无论如何,我们都在为此付费。

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

段落数量不匹配