I have two methods that basically converts underlying checkboxes’ text or tag as CSV strings.
These two methods
- GetSelectedTextAsCsv()
- GetTagAsCsv()
differ only by which property to extract value from SelectedCheckBoxes, which is of type IList<CheckBox>
public string GetSelectedTextAsCsv() { var buffer = new StringBuilder(); foreach (var cb in SelectedCheckBoxes) { buffer.Append(cb.Text).Append(','); } return DropLastComma(buffer.ToString()); } public string GetTagAsCsv() { var buffer = new StringBuilder(); foreach (var cb in SelectedCheckBoxes) { buffer.Append(cb.Tag).Append(','); } return DropLastComma(buffer.ToString()); }
I was trying to extract a method that returns a Func<T, TResult> but not sure how I can pull that off. My poor attempt was like the following but I cannot figure out how to extract the property portion as shown in the comment within ConvertToCsv()
public Func<T, string> ConvertToCsv<T>() { return propertyName => { var buffer = new StringBuilder(); foreach (var checkBox in SelectedCheckBoxes) { buffer.Append( /* How can you abstract this portion? like following? */ checkBox.propertyName ).Append(','); } return DropLastComma(buffer.ToString()); }; }
If I am on a wrong track, would you please advise me on how I can refactor above code to use a common method?
[UPDATE 1] Here is the combination of both Brian and Jon’s answers
public string ConvertToCsv<T>(Func<CheckBox, T> getValue) { var stringValues = SelectedCheckBoxes.Select( cb => getValue(cb).ToString()).ToArray(); return string.Join(',', stringValues); } public string GetSelectedTextAsCsv() { return ConvertToCsv(cb => cb.Text); } public string GetTagAsCsv() { return ConvertToCsv(cb => cb.Tag); }
[UPDATE 2] version 2
public string GetAsCsv<T>(Func<CheckBox, T> getValue) { return string.Join(',', SelectedCheckBoxes.Select( cb => getValue(cb).ToString()).ToArray()); } public string GetSelectedTextAsCsv() { return GetAsCsv(cb => cb.Text); } public string GetTagAsCsv() { return GetAsCsv(cb => cb.Tag == null ? string.Empty : cb.Tag.ToString()); }
[UPDATE 3] Made the parameter of GetAsCsv() as a closed generic of CheckBox and string
Func<CheckBox, T>toFunc<CheckBox, string>.
That allowed me to make GetAsCsv() even simpler and more readable.
private string GetAsCsv(Func<CheckBox, string> getValue) { return string.Join(',', SelectedCheckBoxes.Select(getValue).ToArray()); }
Then: