我正在尝试使用泛型,我正在尝试创建类似于数据集类的结构 我有以下代码
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生成部分类( )只会调用该类包含的列。
一些有用的链接:
T4 Editor for VS
Blog with links and tutorials about T4
Blog entry with example that uses EnvDTE to read project files
答案 0 :(得分:1)
正如您要求使用函数式语言的示例;在lisp中,您可以通过使用宏来为您创建代码,从而阻止在每次添加列时写入所有代码。可悲的是,我不认为在C#中这是可能的。
在性能方面:宏将在编译时进行评估(从而减慢编译的速度),但是在运行时不会导致运行时代码变慢,因为运行时代码与您的运行时代码相同手动写。
我认为您可能必须接受原始的AcceptChanges(),因为如果您想避免写入克隆版本,则需要直接通过其标识符访问结构。
换句话说:你需要一个程序来为你编写程序,而且我不知道如何在C#中做到这一点,而不是通过将结构切换到类来达到非常长的性能或丢失更多的性能(例如反思)。
答案 1 :(得分:0)
您可以使用反射来迭代成员并调用HasChanges和AcceptChanges。 Record类可以将反射元数据存储为静态,因此不存在每个实例的内存开销。但是,运行时的性能成本将会非常巨大 - 您最终可能会对列进行装箱和拆箱,这会增加成本。
答案 2 :(得分:0)
老实说,听起来你真的希望这些Column
成为类,但不想支付与类相关的运行时成本,所以你试图让它们成为结构体。我认为你不会找到一种优雅的方式去做你想做的事。结构应该是值类型,并且您希望它们的行为类似于引用类型。
您无法有效地将列存储在IColumn
的数组中,因此没有数组方法可以正常工作。编译器无法知道IColumn
数组只会保存结构,事实上,它确实没有帮助,因为仍然有不同类型的结构在那里阻塞。每当有人打电话给AcceptChanges()
或HasChanges()
时,你最终都会打结并克隆你的结构,所以我非常怀疑你的Column
是一个结构而不是一个类正在进行为了节省你的记忆。
但是,你可能可能会将你的Column
直接存储在数组中并使用枚举对其进行索引。 E.g:
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#编译器,所以我无法检查它是否有效,但基本的想法应该有效,即使我没有得到正确的所有细节。但是,我会继续他们上课。无论如何,你都在付钱。
答案 3 :(得分:0)
我能想到做你真正想做的事的唯一方法就是使用Reflection。这仍然是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
}
}
答案 4 :(得分: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);
}