[英]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假设
String
.String
。 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:有几点需要注意:
findXXXByName
was the same as the value provided and didn't bother to update tokens[i]
with itfindXXXByName
返回的元组的第二项与提供的值相同,并且没有费心用它更新tokens[i]
region
is provided nowhere in your example, but I still decided to keep it as isregion
,但我仍然决定保持原样Address
instances just when returning them, which forced me to declare multiple constructors (which you may not currently have)Address
实例,这迫使我声明多个构造函数(您目前可能没有)ClearSplit()
was just removing empty strings from the tokensClearSplit()
只是从标记中删除空字符串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.