PVi*_*itt 1 .net c# data-structures
我有一个包含几个结构的类库,每个结构由几个值和引用类型组成.大多数值类型是必需的,一些值类型和所有引用类型都是可选的.所有结构都是XmlSerializable(必须的).
至于类库是针对移动设备的,我想减少内存占用.我的第一个想法是使用Nullable<T>值类型,但这会使内存大小增加4个字节Nullable<T>.我的第二个想法是将所有可选值类型打包到一个单独的结构中,该结构仅在需要任何成员时进行实例化.但这会迫使我IXmlSerializable在"主要"结构上实施.
有没有其他方法来"缩小"结构?
[编辑]
请原谅这个糟糕的问题.我想我必须澄清一些事情并且更具体:
类库旨在序列化数据信息GPX(GPS交换格式).结构例如是Waypoint或Track.它们具有纬度,经度等必填字段.可选字段为垂直/水平/位置精度稀释,描述,链接.
该库主要针对移动设备,如PDA.RAM很短,但可以使用大量非易失性存储器.
只要没有代码示例就无法显示.我想在开始实施之前考虑几个陷阱.
这是一种在允许Xml序列化的同时积极减少内存开销的技术.
更新:与带有计数构造的标准列表相比,原始内联链表的想法对于1和2条目更有效,但对零,一和两种情况使用固定大小的选项更有效.
条件:
这是基于你知道你真的需要削减内存,因此(因为你尚未进行任何编码)这可能是一个非常不成熟的优化.
此设计也基于可选字段非常罕见.
我使用double作为'占位符',无论哪种格式最好,您都可以表示应该使用的精度/单位.
public class WayPoint
{
// consumes IntPtr.Size fixed cost
private IOptional optional = OptionalNone.Default;
public double Latitude { get; set; }
public double Longitude { get; set; }
public double Vertical
{
get { return optional.Get<double>("Vertical") ?? 0.0; }
set { optional = optional.Set<double>("Vertical", value); }
}
[XmlIgnore] // need this pair for every value type
public bool VerticalSpecified
{
get { return optional.Get<double>("Vertical").HasValue; }
}
public void ClearVertical()
{
optional = optional.Clear<double>("Vertical");
}
public string Description // setting to null clears it
{
get { return optional.GetRef<string>("Description"); }
set { optional = optional.SetRef<string>("Description", value); }
}
// Horizontal, Position, DilutionOfPrecision etc.
}
Run Code Online (Sandbox Code Playgroud)
真正繁重的工作在这里完成:
internal interface IOptional
{
T? Get<T>(string id) where T : struct;
T GetRef<T>(string id) where T : class;
IOptional Set<T>(string id, T value);
IOptional Clear(string id);
}
internal sealed class OptionalNone : IOptional
{
public static readonly OptionalNone Default = new OptionalNone();
public T? Get<T>(string id) where T : struct
{
return null;
}
public T GetRef<T>(string id) where T : class
{
return null;
}
public IOptional Set<T>(string id, T value)
{
if (value == null)
return Clear(id);
return new OptionalWithOne<T>(id, value);
}
public IOptional Clear(string id)
{
return this; // no effect
}
}
Run Code Online (Sandbox Code Playgroud)
固定大小的写入会变得更有趣,没有必要将它们写成结构,因为它们将被装箱以放置在WayPoint类的IOptional字段中.
internal sealed class OptionalWithOne<X> : IOptional
{
private string id1;
private X value1;
public OptionalWithOne(string id, X value)
{
this.id1 = id;
this.value1 = value;
}
public T? Get<T>(string id) where T : struct
{
if (string.Equals(id, this.id1))
return (T)(object)this.value1;
return null;
}
public T GetRef<T>(string id) where T : class
{
if (string.Equals(id, this.id1))
return (T)(object)this.value1;
return null;
}
public IOptional Set<T>(string id, T value)
{
if (string.Equals(id, this.id1))
{
if (value == null)
return OptionalNone.Default;
this.value1 = (X)(object)value;
return this;
}
else
{
if (value == null)
return this;
return new OptionalWithTwo<X,T>(this.id1, this.value1, id, value);
}
}
public IOptional Clear(string id)
{
if (string.Equals(id, this.id1))
return OptionalNone.Default;
return this; // no effect
}
}
Run Code Online (Sandbox Code Playgroud)
然后是两个(你可以根据需要扩展这个想法,但你可以看到代码很快就会变得不愉快.
internal sealed class OptionalWithTwo<X,Y> : IOptional
{
private string id1;
private X value1;
private string id2;
private Y value2;
public OptionalWithTwo(
string id1, X value1,
string id2, Y value2)
{
this.id1 = id1;
this.value1 = value1;
this.id2 = id2;
this.value2 = value2;
}
public T? Get<T>(string id) where T : struct
{
if (string.Equals(id, this.id1))
return (T)(object)this.value1;
if (string.Equals(id, this.id2))
return (T)(object)this.value2;
return null;
}
public T GetRef<T>(string id) where T : class
{
if (string.Equals(id, this.id1))
return (T)(object)this.value1;
if (string.Equals(id, this.id2))
return (T)(object)this.value2;
return null;
}
public IOptional Set<T>(string id, T value)
{
if (string.Equals(id, this.id1))
{
if (value == null)
return Clear(id);
this.value1 = (X)(object)value;
return this;
}
else if (string.Equals(id, this.id2))
{
if (value == null)
return Clear(id);
this.value2 = (Y)(object)value;
return this;
}
else
{
if (value == null)
return this;
return new OptionalWithMany(
this.id1, this.value1,
this.id2, this.value2,
id, value);
}
}
public IOptional Clear(string id)
{
if (string.Equals(id, this.id1))
return new OptionalWithOne<Y>(this.id2, this.value2);
if (string.Equals(id, this.id2))
return new OptionalWithOne<X>(this.id1, this.value1);
return this; // no effect
}
}
Run Code Online (Sandbox Code Playgroud)
在最终以相对低效率结束之前
internal sealed class OptionalWithMany : IOptional
{
private List<string> ids = new List<string>();
// this boxes, if you had a restricted set of data types
// you could do a per type list and map between them
// it is assumed that this is sufficiently uncommon that you don't care
private List<object> values = new List<object>();
public OptionalWithMany(
string id1, object value1,
string id2, object value2,
string id3, object value3)
{
this.ids.Add(id1);
this.values.Add(value1);
this.ids.Add(id2);
this.values.Add(value2);
this.ids.Add(id3);
this.values.Add(value3);
}
public T? Get<T>(string id) where T : struct
{
for (int i= 0; i < this.values.Count;i++)
{
if (string.Equals(id, this.ids[i]))
return (T)this.values[i];
}
return null;
}
public T GetRef<T>(string id) where T : class
{
for (int i= 0; i < this.values.Count;i++)
{
if (string.Equals(id, this.ids[i]))
return (T)this.values[i];
}
return null;
}
public IOptional Set<T>(string id, T value)
{
for (int i= 0; i < this.values.Count;i++)
{
if (string.Equals(id, this.ids[i]))
{
if (value == null)
return Clear(id);
this.values[i] = value;
return this;
}
}
if (value != null)
{
this.ids.Add(id);
this.values.Add(value);
}
return this;
}
public IOptional Clear(string id)
{
for (int i= 0; i < this.values.Count;i++)
{
if (string.Equals(id, this.ids[i]))
{
this.ids.RemoveAt(i);
this.values.RemoveAt(i);
return ShrinkIfNeeded();
}
}
return this; // no effect
}
private IOptional ShrinkIfNeeded()
{
if (this.ids.Count == 2)
{
//return new OptionalWithTwo<X,Y>(
// this.ids[0], this.values[0],
// this.ids[1], this.values[1]);
return (IOptional)
typeof(OptionalWithTwo<,>).MakeGenericType(
// this is a bit risky.
// your value types may not use inhertence
this.values[0].GetType(),
this.values[1].GetType())
.GetConstructors().First().Invoke(
new object[]
{
this.ids[0], this.values[0],
this.ids[1], this.values[1]
});
}
return this;
}
}
Run Code Online (Sandbox Code Playgroud)
OptionalWithMany可以写得比这更好,但它给你的想法.使用受限制类型支持,您可以为每个类型的"堆"执行全局键 - >值映射,如下所示:
internal struct Key
{
public readonly OptionalWithMany;
public readonly string Id;
// define equality and hashcode as per usual
}
Run Code Online (Sandbox Code Playgroud)
然后只需在OptionalToMany中存储当前正在使用的Id列表.缩小会稍微复杂一些(但从类型的角度来看更好,因为你会扫描每个全局'堆',直到找到匹配的条目并使用堆的类型来构造OptionalWithTwo.这将允许属性值中的多态性.
无论内部结构如何,主要的好处是WayPoint类的公共表面完全隐藏了这一切.
然后,您可以设置类,但是您希望通过属性IXmlSerializable进行序列化(这将消除烦人的xxxSpecified属性的需要).
在我的例子中,我使用字符串作为Id来简化.
如果您真的关心大小和速度,您应该将Id更改为枚举.给定打包行为,即使您可以将所有需要的值放入一个字节中,这也不会为您节省太多,但它会为您提供编译时的健全性检查.字符串都是编译时常量,因此占用的空间旁边没有空格(但是检查相等性的速度较慢).
我建议你在检查完需要之后才做这样的事情.好的一面是,这并不限制你的xml序列化,所以你可以把它塑造成你想要的任何格式.此外,"数据包"的公共面可以保持清洁(除了xxxSpecified垃圾).
如果你想避免xxxSpecified麻烦,你知道你有一些'带外'值,你可以使用以下技巧:
[DefaultValue(double.MaxValue)]
public double Vertical
{
get { return optional.Get<double>("Vertical") ?? double.MaxValue; }
set { optional = optional.Set<double>("Vertical", value); }
}
public void ClearVertical()
{
optional = optional.ClearValue<double>("Vertical");
}
Run Code Online (Sandbox Code Playgroud)
但是,其余的API必须能够检测这些特殊值.一般来说,我会说指定的路线更好.
如果某些特定属性集在某些设备上变为"始终可用",或者在某些模式下,您应切换到其中属性为简单类的备用类.由于xml表单将是相同的,这意味着它们可以简单轻松地进行互操作,但在这些情况下的内存使用将会少得多.
如果这些组的数量变大,您甚至可以考虑代码生成方案(甚至在运行时,尽管这会大大增加您的支持负担)
| 归档时间: |
|
| 查看次数: |
1143 次 |
| 最近记录: |