简体   繁体   English

如何避免使用引用类型的重复代码?

[英]How can I avoid repetitive code using reference types?

I've made a method to split address line into separate distinct categories like Region, City, Street and House which are represented by a single class. It follows relatively simple structure.我已经制作了一种方法,将地址行拆分为单独的不同类别,如地区、城市、街道和房屋,它们由单个 class 表示。它遵循相对简单的结构。

Address useOther(string Casual, string Other)
    {
        Address
            address = new Address();

        List<string>
            split = new List<string>();
        char[]
            separator = { ',' };
        split.AddRange(
            Casual.Split(
                separator, 
                StringSplitOptions.RemoveEmptyEntries));
        split.AddRange(
            Other.Split(
                separator, 
                StringSplitOptions.RemoveEmptyEntries));

        // Initialize parameters
        split = RemoveBannedTokens(split);

        for (int i = 0; i < split.Count; i++)
        {
            (address.Region, split[i]) = findRegionByName(split[i]);

            if (address.Region != null)
            {
                split[i] = split[i].Replace(address.Region.Name, "");
                break;
            }
        }

        if (address.Region == null)
        {
            address.Region = new Region(region);
        }

        split = ClearSplit(split);

        // Finding region
        for (int i = 0; i < split.Count; i++)
        {
            (address.City, split[i]) = findSityByName(split[i]);

            if (address.City != null)
            {
                split[i] = split[i].Replace(address.City.Name, "");
                break;
            }

        }
        if (address.City == null)
        {
            for (int i = 0; i < split.Count; i++)
            {
                (address.City, split[i]) = findCityByName(split[i], false);

                if (address.City != null)
                {
                    split[i] = split[i].Replace(address.City.Name, "");
                    break;
                }

            }
        }
        if (address.City == null)
            return address;
        split = ClearSplit(split);

        // Finding city
        for (int i = 0; i < split.Count; i++)
        {
            (address.Street, split[i]) = findStreetByName(split[i], address.City);

            if (address.Street != null)
            {
                split[i] = split[i].Replace(address.Street.Name, "");
                break;
            }
        }
        if (address.Street == null && 
            (address.Region.Code.Replace("/", "") + 
             address.City.Code.Replace("/", "")).Length != 13)
            return address;
        split = ClearSplit(split);
        //Finding street

        int
            cityIndex = address.City.Index,
            streetIndex = address.Street.Index;

        for (int i = 0; i < split.Count; i++)
        {
            split[i] = split[i].Replace("б/н", "");
        }
        address.House = findNumbers(split.ToArray(), cityIndex, streetIndex);

        return address;
    }
    

If you can see repetition goes like this如果你能看到重复是这样的

for (int i = 0; i < split.Count; i++)
{
    (address.Something, split[i]) = findSomethingByName(split[i]);

    if (address.Something != null)
    {
        //do this thing
        break;
    }

}
if (address.Something == null)
    //try other thing
split = ClearSplit(split);

I want to avoid this repetition and have something of an array or foreach loop but couldn't find a clear way of making an array with reference values or a foreach reference loop that would work with null values properly.我想避免这种重复,并有一些数组或 foreach 循环,但找不到一种明确的方法来制作具有引用值的数组或可以正确处理 null 值的 foreach 引用循环。 I have thought about making a function with specified inputs and outputs but decide it would only harm ability to read my code, which I'm trying to improve here.我曾考虑过制作一个具有指定输入和输出的 function,但认为它只会损害阅读我的代码的能力,我正在努力改进。

Let me try to demonstrate your problem and my proposal.让我试着证明你的问题和我的建议。

Assumptions假设

  1. Your class has several similiar properties, all with value of String .您的 class 有几个类似的属性,所有属性的值为String
  2. You have to keep the properties, because they may be required by other frameworks.您必须保留这些属性,因为其他框架可能需要它们。
  3. You have repeative code to work with the properties.您有重复代码来处理这些属性。

OldAddress旧地址

public class OldAddress
{
    public string City { get; set; }
    public string Region { get; set; }
}

Old Program旧程序

public class Program
{

    static OldAddress oldAddress;

    public static string GetFromDataSource(string prop)
    {
        return null;//should return your real value
    }

    public static void AssignValueOldWay()
    {
        if (oldAddress.City != null)
        {
            oldAddress.City = GetFromDataSource("City");
        }
        if (oldAddress.Region != null)
        {
            oldAddress.Region = GetFromDataSource("Region");
        }
        // more...
    }
}

NewAddress新地址

public class NewAddress
{
    public static string[] PropNames = { "City", "Region" };
    public NewAddress()
    {
        props = new Dictionary<string, string>();
        foreach (string prop in PropNames)
        {
            props.Add(prop, "");
        }
    }

    public string City
    {
        get
        {
            return GetProperty("City");
        }
        set
        {
            SetProperty("City", value);
        }
    }
    public string Region
    {
        get
        {
            return GetProperty("Region");
        }
        set
        {
            SetProperty("Region", value);
        }
    }

    private Dictionary<string, string> props;

    public void SetProperty(string prop, string value)
    {
        props[prop] = value;
    }

    public string GetProperty(string prop)
    {
        return props[prop];
    }

}

New Program新节目

public class Program
{

    public static string GetFromDataSource(string prop)
    {
        return null;//should return your real value
    }

    static NewAddress newAddress;

    public static void AssignValueNewWay()
    {
        foreach (string prop in NewAddress.PropNames)
        {
            if (newAddress.GetProperty(prop) != null)
            {
                newAddress.SetProperty(prop, GetFromDataSource(prop));
            }
        }
    }
}

Note my code is just to demonstrate the idea, not optimized for runtime exceptions or bad styles such as public fields, or magic strings.请注意,我的代码只是为了演示这个想法,并未针对运行时异常或公共字段或魔术字符串等错误 styles 进行优化。

After some thinking and looking on how I could manage this, I have decided to follow JAlex advise.经过一番思考并研究如何解决这个问题后,我决定听从 JAlex 的建议。 For now I don't think this change is necessary, so I leave it be for now.目前我认为没有必要进行此更改,所以我暂时保留它。 What I will do is I would try make this more readable by using additional spaces and comments.我要做的是尝试通过使用额外的空格和注释来使其更具可读性。 As of for now my method looks something like this (after a bit of tweaking here and there) and I'm more or less happy with how readable it is到目前为止,我的方法看起来像这样(在这里和那里进行了一些调整之后),我或多或少对它的可读性感到满意

Address useOther(string Casual, string Other)
{
    Address address = 
        new Address();
    //Initialize address

    List<string> split = 
        new List<string>();
    char[] separator = 
    { 
        ',' 
    };
    //Initialize split and separators

    split.AddRange(Casual.Split(separator));
    split.AddRange(Other .Split(separator));
    RemoveBannedTokens(ref split);
    //Fill split



    for (int i = 0; i < split.Count; i++)
    {
        (address.Region, split[i]) = 
            findRegionByName(split[i]);

        if (address.Region != null)
            break;
    }
    //Trying to find region

    if (address.Region == null)
        address.Region = new Region(region);
    //If Region is not found

    ClearSplit(ref split);
    //Get rid of empty strings



    for (int i = 0; i < split.Count; i++)
    {
        (address.City, split[i]) = 
            findSityByName(split[i]);

        if (address.City != null)
            break;
    }
    //Trying to find city

    if (address.City == null)
    {
        for (int i = 0; i < split.Count; i++)
        {
            (address.City, split[i]) = 
                findSityByName(split[i], false);

            if (address.City != null)
                break;
        }
    }
    //Trying to find city with additional params

    if (address.City == null)
        return address;
    //If City is not found

    ClearSplit(ref split);
    //Get rid of empty strings



    for (int i = 0; i < split.Count; i++)
    {
        (address.Street, split[i]) = 
            findStreetByName(split[i], address.City);

        if (address.Street != null)
            break;
    }
    //Trying to find street

    string code =
        address.Region.Code.Replace("/", "") +
        address.City.Code  .Replace("/", "");
    //Initialize code

    if (address.Street == null && code.Length != 13)
        return address;
    //If Street is not found and address is not complete

    ClearSplit(ref split);
    //Finding street

    address.House = findNumbers(split);
    //Trying to find house

    return address;
}

I had some spare time, so I decided to refactor your code as a mental exercise for myself.我有一些空闲时间,所以我决定重构你的代码,作为我自己的一种心理锻炼。

I still wanted to post my solution in case you are interested in seeing it.我仍然想发布我的解决方案,以防您有兴趣看到它。 The most fundamental shift is that I oriented my thoughts from imperative programming to functional programming which resulted in a more concise code, and for people used to this paradigm, a much more readable code.最根本的转变是我将我的思想从命令式编程转向函数式编程,这导致了更简洁的代码,并且对于习惯这种范式的人来说,代码更具可读性。

Address userOther(string Casual, string Other)
{
    var tokens = RemoveBannedTokens(Casual.Split(',', StringSplitOptions.RemoveEmptyEntries)
                                          .Concat(Other.Split(',', StringSplitOptions.RemoveEmptyEntries))
                                          .ToList());

    //Region
    var regionResult = tokens.Select(findRegionByName).FirstOrDefault(t => t.Item1 != null);
    regionResult = regionResult == default ? (region, string.Empty) : regionResult;
    tokens.Remove(regionResult.Item2);

    //City
    var cityResult = tokens.Select(findSityByName).FirstOrDefault(t => t.Item1 != null);
    cityResult = cityResult == default ? tokens.Select(t => findCityByName(t, false)).FirstOrDefault(t => t.Item1 != null) : cityResult;
    cityResult = cityResult == default ? (default(City), string.Empty) : cityResult;
    if (cityResult.Item1 == null)
    {
        return new Address(regionResult.Item1);
    }
    tokens.Remove(cityResult.Item2);

    //Street
    var streetResult = tokens.Select(t => findStreetByName(t, cityResult.Item1)).FirstOrDefault(t => t.Item1 != null);
    if (streetResult.Item1 == null &&
        (regionResult.Item1.Code.Replace("/", "") + cityResult.Item1.Code.Replace("/", "")).Length != 13)
    {
        return new Address(regionResult.Item1, cityResult.Item1);
    }
    tokens.Remove(streetResult.Item2);

    return new Address(regionResult.Item1,
                       cityResult.Item1,
                       streetResult.Item1,
                       findNumbers(tokens.Select(t => t.Replace("б/н", "")).ToArray(), cityResult.Item1.Index, streetResult.Item1.Index));
}

A few things to note:有几点需要注意:

  • Since you didn't provided some methods used by this one, I was unable to test the real behavior of this approach由于您没有提供此方法使用的一些方法,因此我无法测试此方法的真实行为
  • I assumed that the returned second item of tuple from methods findXXXByName was the same as the value provided and didn't bother to update tokens[i] with it我假设从方法findXXXByName返回的元组的第二项与提供的值相同,并且没有费心用它更新tokens[i]
  • the variable region is provided nowhere in your example, but I still decided to keep it as is在您的示例中没有提供可变region ,但我仍然决定保持原样
  • I created Address instances just when returning them, which forced me to declare multiple constructors (which you may not currently have)我在返回它们时创建了Address实例,这迫使我声明多个构造函数(您目前可能没有)
  • I assumed that ClearSplit() was just removing empty strings from the tokens我假设ClearSplit()只是从标记中删除空字符串

I hope you will find this bit of code valuable for your career path and personnal learning.我希望您会发现这段代码对您的职业道路和个人学习很有价值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM