Проблема проектирования, приводящая к невозможности получить правильный dataType - PullRequest
0 голосов
/ 18 июля 2009

У меня есть объект, у которого есть свойства другого объекта, который называется DataValue, но тип, который я хочу DataValue, зависит от информации, содержащейся в объекте в другом свойстве. Я не уверен, что мой путь - лучший способ сделать это.

У меня есть этот бизнес-объект, который называется AssetStructure.

Объект AssetStructure содержит общий список объектов IAssetStructureField, представляющих собой серию объектов, которые в основном содержат информацию о данных, которые могут храниться в этом поле, значение по умолчанию определенного типа данных и некоторые отображаемые информационные свойства. Каждый из объектов, реализующих интерфейс IAssetStructureField, будет содержать разные типы данных. Например, один тип DefaultValue может быть строкой, а другой - List<ofCustomType>.

У меня есть объект Asset, содержащий общий список объектов, который называется AssetDataField. AssetDataField имеет свойства одного, содержащего AssetStructureField, и одного, называемого DataValue, данных актива для этого StructureField.

Моя проблема связана с типом свойства AssetDataField DataValue, оно должно быть другим в зависимости от деталей объекта AssetStructureField. Этот StructureField может содержать данные, представляющие все группы пользователей с доступом к Активу (тип данных List<UserGroups>), а другой может быть просто полем описания (строка типа данных), поэтому мне нужно, чтобы DataValue, выходящий из 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 действовал. Спасибо за любые идеи, которые вы можете предоставить.

Ответы [ 2 ]

0 голосов
/ 18 июля 2009

Примечание: это пахнет как результат запроса системы на основе 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 как double, несмотря на то, что преобразование работает на распакованных экземплярах.
0 голосов
/ 18 июля 2009

Похоже, вы должны сделать 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"));
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...