设计问题导致无法返回正确的dataType

时间:2009-07-17 22:09:24

标签: c# design-patterns generics architecture

我有一个具有另一个对象属性的对象和一个名为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正在起作用。感谢您提供的任何见解。

2 个答案:

答案 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!");
    }
}
  1. 至少没有不安全的代码或工会(只有结构)。
  2. 这不仅会对性能产生影响,例如,尽管转换工作在未装箱的实例上,但您无法将int取消装箱为双倍。