簡體   English   中英

以類型安全的方式遍歷對象層次結構

[英]Walking object hierarchy in a typesafe manner

在工作中,我們有一個非常復雜的數據結構TComplex:

public class TComplex
{
    public AType AType { get; set; }
    public BType BType { get; set; }
    public IList<CType> CTypes { get; set; }
}

public class AType
{
    public int AInteger { get; set; }
    public int BInteger { get; set; }
    public string AString { get; set; }
}

public class BType
{
    public string AString { get; set; } 
    public IList<DType> DTypes { get; set; }
}

public class CType
{
    public int Id { get; set; }
    public string Value { get; set; }
}

public class DType 
{
    public string Value { get; set; }   
}

最近通過將枚舉ParsedValue添加到幾種節點類型進行了修改。

public enum ParsedValue
{
    Parsed,
    Interpreted,
    Guessed,
    Explicit
}

public class TComplex
{
    public AType AType { get; set; }
    public BType BType { get; set; }
    public IList<CType> CTypes { get; set; }
}

public class AType
{
    public int AInteger { get; set; }
    public int BInteger { get; set; }
    public string AString { get; set; }
    public ParsedValue ParsedValue { get; set; }
}

public class BType
{
    public string AString { get; set; } 
    public IList<DType> DTypes { get; set; }
    public ParsedValue ParsedValue { get; set; }
}

public class CType
{
    public int Id { get; set; }
    public string Value { get; set; }
    public ParsedValue ParsedValue { get; set; }
}

public class DType 
{
    public string Value { get; set; }   
}

首先,給定類型為TComplex的對象,我將如何遍歷層次結構並產生ParsedValue的所有值:

public static IEnumerable<ParsedValue> GetParsedValues(TComplex tComplex)

更進一步,我如何將這個對象中的所有ParsedValues設置為特定的ParsedValue ,比如說ParsedValue.Explicit:

public static IEnumerable<Action<ParsedValue>> SetParsedValues(TComplex tComplex)

我有一個天真的GetParsedValues和SetParsedValues實現。 但是,如果要向層次結構中的其他類型添加ParsedValue,則還需要更改GetParsedValues和SetParsedValues的實現,但我發現它們很脆弱:

public static IEnumerable<ParsedValue> GetParsedValues(TComplex tComplex)
{
    yield return tComplex.AType.ParsedValue;
    yield return tComplex.BType.ParsedValue;

    foreach(var cType in tComplex.CTypes)
    {
        yield return cType.ParsedValue;
    }
}

public static IEnumerable<Action<ParsedValue>> SetParsedValues(TComplex tComplex)
{
    yield return value => tComplex.AType.ParsedValue = value;
    yield return value => tComplex.BType.ParsedValue = value;

    foreach(var cType in tComplex.CTypes)
    {
        yield return value => cType.ParsedValue = value;
    }
}

我考慮過反射,但是有更好的方法嗎?

實際上,我看不到任何令人反感的方式,但是如果您願意的話,可以稍微清理一下。

一種選擇是為所有類而不是接口編寫通用基類ParsedValueProviderBase 為它提供相同兩種方法的虛擬無操作實現:

public virtual IEnumerable<ParsedValue> GetParsedValues() 
    => Enumerable.Empty<ParsedValue>();

public virtual IEnumerable<ParsedValue> SetParsedValues(ParsedValue pv) 
    => Enumerable.Empty<ParsedValue>();

子類可以覆蓋或不覆蓋它們。

您還可以編寫一個IParsedValueThing接口,該接口由部分或全部類實現:

public interface IParsedValueThing {
    IEnumerable<ParsedValue> GetParsedValues();
    IEnumerable<ParsedValue> SetParsedValues(ParsedValue pv);
}

讓大家實現該接口。 其中一些就是這樣:

public IEnumerable<ParsedValue> GetParsedValues() 
    => yield return ParsedValue;

public IEnumerable<ParsedValue> SetParsedValues(ParsedValue pv) 
    => yield return ParsedValue = pv;

其他人會遞歸給孩子,等等。

如果您可能在這些類之外的其他所有地方都使用該接口,那可能是個好主意。 您可以編寫接口並在ParsedValueProviderBase實現它。

為了使這個更通用,您可以讓每個類實現一個GetParsedValues和SetParsedValues函數,在您的復雜類型中,您現在可以依賴這些函數的實現,並確保它們返回您想要中繼的內容。

在這里,我僅實現了GetParsedValues部分,同樣可以應用於SetParsedValues

public class TComplex
{
    public AType AType { get; set; }
    public BType BType { get; set; }
    public IList<CType> CTypes { get; set; }
    public IEnumerable<ParsedValue> GetParsedValues()
    {
        foreach (var parsedValue in AType.GetParsedValues())
            yield return parsedValue;
        foreach (var parsedValue in BType.GetParsedValues())
            yield return parsedValue;

        foreach (var cType in CTypes)
            foreach (var parsedValue in cType.GetParsedValues())
                yield return parsedValue;
    }
}

public class AType
{
    public int AInteger { get; set; }
    public int BInteger { get; set; }
    public string AString { get; set; }
    public ParsedValue ParsedValue { get; set; }
    public IEnumerable<ParsedValue> GetParsedValues()
    {
        yield return ParsedValue;
    }
}

public class BType
{
    public string AString { get; set; } 
    public IList<DType> DTypes { get; set; }
    public ParsedValue ParsedValue { get; set; }
    public IEnumerable<ParsedValue> GetParsedValues()
    {
        yield return ParsedValue;
    }
}

public class CType
{
    public int Id { get; set; }
    public string Value { get; set; }
    public ParsedValue ParsedValue { get; set; }
    public IEnumerable<ParsedValue> GetParsedValues()
    {
        yield return ParsedValue;
    }
}

如您所見,可以修改類型並只編輯該類型的功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM