C# 设计问题导致无法返回正确的数据类型

C# 设计问题导致无法返回正确的数据类型,c#,design-patterns,generics,architecture,C#,Design Patterns,Generics,Architecture,我有一个对象具有另一个对象的属性,一个称为DataValue,但我希望DataValue返回的类型取决于另一个属性中对象中包含的信息。我不相信我的方法是最好的方法 我有一个叫做资产结构的业务对象 AssetStructure对象包含IAssetStructureField对象的通用列表,这些对象是一系列基本上包含有关可在该字段中保存的数据的信息、特定数据类型的默认值和一些显示信息属性的对象。实现IAssetStructureField接口的每个对象将持有不同的数据类型。例如,一个DefaultV

我有一个对象具有另一个对象的属性,一个称为
DataValue
,但我希望
DataValue
返回的类型取决于另一个属性中对象中包含的信息。我不相信我的方法是最好的方法

我有一个叫做资产结构的业务对象

AssetStructure对象包含IAssetStructureField对象的通用列表,这些对象是一系列基本上包含有关可在该字段中保存的数据的信息、特定数据类型的默认值和一些显示信息属性的对象。实现IAssetStructureField接口的每个对象将持有不同的数据类型。例如,一个DefaultValue的类型可能是string,另一个可能是
列表

我的资产对象包含一个名为AssetDataField的通用对象列表。AssetDataField具有一个包含AssetStructureField的属性和一个名为DataValue的属性,DataValue是该StructureField的资产数据

我的问题是AssetDataField DataValue属性的数据类型,它需要根据AssetStructureField对象的详细信息而有所不同。此StructureField可能包含表示有权访问资产的所有用户组的数据(datatype
List
),而另一个可能只是一个描述字段(datatype string),因此我需要AssetDataField中的数据值具有相同的类型

我现在想做的是,让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出现了问题。感谢您提供的所有见解。

似乎您应该将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"));
    }
}
类程序
{
静态void Main(字符串[]参数)
{
资产证书资产=新资产(32423);
foreach(certainAsset.DataFields中的AssetDataField数据字段)
{
dataField.Deal();
}
}
}
类别资产
{
公共列表数据字段=新列表();
公共资产(int id)
{
//加载资源#id
如果(id==32423)
{
添加(新的AssetDataFieldString());
添加(新的AssetDataFieldFloat());
}
}
}
抽象类AssetDataField
{
公共资产数据字段()
{
FieldValue=默认值;
}
公共抽象对象默认值{get;}
公共抽象对象字段值{get;set;}
公开抽象无效交易();
}
抽象类AssetDataFieldType:AssetDataField
{
保护T值;
公共覆盖对象字段值
{
得到
{
返回类型值;
}
设置
{
TypedValue=(T)System.Convert.ChangeType(value,typeof(T));
}
}
公共虚拟T类型值
{
得到
{
返回内部值;
}
设置
{
内在价值=价值;
}
}
}
类AssetDataFieldString:AssetDataFieldType
{
公共覆盖对象默认值
{
获取{返回“默认字符串”;}
}
//可选覆盖TypedValue
公共覆盖无效交易()
{
控制台写入线(TypedValue.PadLeft(20));
}
}
类AssetDataFieldFloat:AssetDataFieldType
{
公共覆盖对象默认值
{
获取{返回0;}
}
//可选覆盖TypedValue
公共覆盖无效交易()
{
Console.WriteLine(TypedValue.ToString(#0.000”);
}
}

注意:这闻起来像是查询基于EAV的系统的结果。正如元数据是此类系统的主干一样,引用元数据的代码应该努力了解它在编译时访问的内容(以及类型)。也就是说,如果你想简单地显示数据,不管发生什么,都需要这种东西

C#是静态类型的,因此不能将“不同的东西”放入同一个“槽”(变量,数组位置),除非槽是正确的“形状”,可以容纳所有的东西(1)。目前c#中唯一可用的插槽是对象。这将起作用,但将框选任何值类型(2)

在c#4.0中,您可以使用dynamic,它在引擎盖下是一个对象,但至少允许您调用它上的任何方法,即使编译器认为它通过object是不合法的

如果所有相关类型共享一个公共接口,那么您可以避免对象并获得一些有用的语义(例如,如果
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);
     }
}
  • 至少没有不安全的代码或联合(仅结构)
  • 这不仅对性能有影响,例如,不能将int作为double取消装箱,尽管这种转换在未装箱的实例上工作

  • 我为了冗长而提供清晰性的尝试失败了吗?
    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!");
        }
    }