我有一个具有另一个对象属性的对象和一个名为DataValue
的对象,但我希望DataValue
返回的类型取决于另一个属性中对象中包含的信息。我不相信我的方式是最好的方法。
我有一个名为AssetStructure的业务对象。
AssetStructure对象包含IAssetStructureField对象的通用列表,这些对象是一系列对象,基本上包含有关该字段中可保存的数据的信息,某个数据类型的默认值以及一些显示信息属性。实现IAssetStructureField接口的每个对象将保持不同的数据类型。例如,一个人的DefaultValue类型可能是字符串,另一个可能是List<ofCustomType>
。
我的Asset对象包含一个名为AssetDataField的通用对象列表。 AssetDataField具有包含AssetStructureField的属性和一个名为DataValue的属性,DataValue是该StructureField的Asset的数据。
我的问题是AssetDataField DataValue属性的数据类型,它需要根据AssetStructureField对象的详细信息而有所不同。此StructureField可以保存表示可以访问Asset(数据类型List<UserGroups>
)的所有用户组的数据,而另一个可能只是一个描述字段(数据类型字符串),因此我需要来自AssetDataField的DataValue属于相同的类型。
我现在正在考虑做什么,并且我认为可以做得更好,就是让AssetDataField.DataValue返回一个对象,然后将其转换为AssetDataField.StructureField.DefaultValue的类型。
object fieldValue;
object fieldDefaultValue;
Asset certainAsset = new Asset(32423);
foreach (AssetDataField dataField in certainAsset.DataFields)
{
fieldDefaultValue = datafield.StructureField.DefaultValue;
fieldValue = datafield.DataValue as typeof(fieldDefaultValue);
// then do stuff depending on what typeof(fieldValue) is. This is where I
// see things getting particularly ugly. Not only just because that
// this class here will need to know the possible types that may be
// returned, so it can deal.
if (typeof(fieldValue) == whatever)
{
// deal;
}
else if (typeof(fieldValue) == whatever2)
{
// deal differently;
}
}
有没有人有任何建议?我根本不反对完全重做。我真的很抱歉这是如此啰嗦,我只是想尝试解释一下情况。我试图整理一个UML图来帮忙,但是我的ArgoUML正在起作用。感谢您提供的任何见解。
答案 0 :(得分:0)
您似乎应该将AssetDataField设置为可能是抽象的基类,并从中派生其他类来执行工作。例如:
class Program
{
static void Main(string[] args)
{
Asset certainAsset = new Asset(32423);
foreach (AssetDataField dataField in certainAsset.DataFields)
{
dataField.Deal();
}
}
}
class Asset
{
public List<AssetDataField> DataFields = new List<AssetDataField>();
public Asset(int id)
{
// Load asset #id
if (id == 32423)
{
DataFields.Add(new AssetDataFieldString());
DataFields.Add(new AssetDataFieldFloat());
}
}
}
abstract class AssetDataField
{
public AssetDataField()
{
FieldValue = DefaultValue;
}
public abstract object DefaultValue { get; }
public abstract object FieldValue { get; set; }
public abstract void Deal();
}
abstract class AssetDataFieldType<T> : AssetDataField
{
protected T internalValue;
public override object FieldValue
{
get
{
return TypedValue;
}
set
{
TypedValue = (T)System.Convert.ChangeType(value, typeof(T));
}
}
public virtual T TypedValue
{
get
{
return internalValue;
}
set
{
internalValue = value;
}
}
}
class AssetDataFieldString : AssetDataFieldType<string>
{
public override object DefaultValue
{
get { return "Default String"; }
}
// Optionally override TypedValue
public override void Deal()
{
Console.WriteLine(TypedValue.PadLeft(20));
}
}
class AssetDataFieldFloat : AssetDataFieldType<float>
{
public override object DefaultValue
{
get { return 0; }
}
// Optionally override TypedValue
public override void Deal()
{
Console.WriteLine(TypedValue.ToString("#0.000"));
}
}
答案 1 :(得分:0)
注意:这有点像查询基于EAV的系统的结果。与元数据是这种系统的主干一样,引用它的代码应该努力在编译时知道它正在访问什么(以及类型)。也就是说,如果你想简单地显示数据,无论如何都需要这样的东西。
C#是静态类型的,因此您不能将“不同的东西”放入相同的“插槽”(变量,数组位置),除非插槽是正确的“形状”以获取所有这些(1)。目前c#中唯一可用的插槽就是对象。这将有效但将包装任何值类型(2)。
在c#4.0中,你可以使用动态,它可以是一个对象,但至少可以让你调用你想要的任何方法,即使编译器认为它不是通过对象合法的。
如果所讨论的所有类型共享一个公共接口,那么你可以避免使用对象并获得一些有用的语义(比如说double Sum(double d)
对于你正在处理的任何实例来说是一个有意义的操作,那么这可能会产生有用的结果。但是听起来你不能控制存在的类型(因此没有机会让它们符合有用的接口)。
如果可能类型的集合易于处理,下面描述的技术可以工作,但仍然很麻烦。
// boxes if needed
public interface IGeneralValue
{
object Value { get; }
Type GetValueType();
}
public class Value<T> : IGeneralValue
{
public T Value { get; set;}
object IGeneralValue.Value
{
get { return (object)this.Value; }
}
public Type GetValueType()
{
return typeof(T);
}
}
然后你可以在可能的情况下保持静态输入,但如果没有类似于你以前的代码的话,那就可以了。
Asset certainAsset = new Asset(32423);
foreach (IGeneralValue dataField in certainAsset.DataFields)
{
object fieldValue = datafield.Value;
Type fieldType = dataField.GetValueType();
if (typeof(double).Equals(fieldType))
{
double d = ((double)fieldValue);
}
else if (typeof(string).Equals(fieldType))
{
string d = ((string)fieldValue);
}
else if (typeof(whatever).Equals(fieldType))
{
// deal with whatever
}
else
{
// the safe option
throw new NotSupportedException(fieldType +" is not supported!");
}
}