How do I make these properties that do the same thing DRY? - c#

My class has 3 properties that have to do the same thing in the custom set that I made.
Now I have this repeated in all 3, which isn't DRY.
How do I make this DRY? Just put the foreach into a method? I feel like there must be a more elegant way.
(also I wish I didn't need the private backing fields because they are kind of an eye-sore)
private List<string> _ImageTypes;
public List<string> ImageTypes
{
get { return _ImageTypes; }
set
{
_ImageTypes = new List<string>();
foreach (var type in value)
if (!string.IsNullOrEmpty(type))
_ImageTypes.Add("." + type.Replace(".", "").Replace("*", ""));
}
}
private List<string> _AnimationTypes;
public List<string> AnimationTypes
{
get { return _AnimationTypes; }
set
{
_AnimationTypes = new List<string>();
foreach (var type in value)
if (!string.IsNullOrEmpty(type))
_AnimationTypes.Add("." + type.Replace(".", "").Replace("*", ""));
}
}
private List<string> _VideoTypes;
public List<string> VideoTypes
{
get { return _VideoTypes; }
set
{
_VideoTypes = new List<string>();
foreach (var type in value)
if (!string.IsNullOrEmpty(type))
_VideoTypes.Add("." + type.Replace(".", "").Replace("*", ""));
}
}

Yes, put it in the method
private List<string> CreateListFrom(List<string> list)
{
return list.Where(type => !string.IsNullOrEmpty(type))
.Select(type => type.Replace(".", "").Replace("*", ""))
.Select(type => $".{type}")
.ToList();
}
Then use it in the setters
private List<string> _ImageTypes;
public List<string> ImageTypes
{
get { return _ImageTypes; }
set
{
_ImageTypes = CreateListFrom(value);
}
}
Another approach - do it in the constructor, then you can get rid of private members.
But this will depend on how this class is consumed
Before talking about DRY - you should be sure that similar looking code are going to be changed for same reasons.

Related

Assert.That() does not evaluate given constraint

I have a list of string values. I iterate this list with a foreach. I apply on every list item Assert.That() method. Now comes the twist: I give as a second parameter of the method a function that returns IResolveConstraint. When the function returns Has.Exactly(1).Contains(), then AT THE SECOND item of the list (second iteration of the foreach) the assertion evaluates (throws) this message: "Expected: collection containing "value"". But the assertion was supposed to pass, because it was verifying exactly one item, not a collection.
I had NUnit 3.2.1. I upgraded to version 3.12.0 and after that the message changed from : "Expected: collection containing "value"" to message: "Expected: some item equal to "value"" and assertion still didn' pass.
public class Verifiers
{
public void VerifyCollectedValues(List<string> collectedValues, List<string> expectedValues
, string collectedValuesFrom, int countOfValueExpactance, bool verifyExactSameValue = true)
{
// Create a constraint function
Func<string, IResolveConstraint> constraintFunction = CreateConstraintFunction(Has.Exactly(countOfValueExpactance), verifyExactSameValue);
// Pass the constraint to the method
VerifyCollectedValues(constraintFunction, collectedValues, expectedValues, collectedValuesFrom);
}
public void VerifyCollectedValues(Func<string, IResolveConstraint> constraintFunction, List<string> collectedValues, List<string> expectedValues
, string collectedValuesFrom)
{
foreach (string expectedValue in expectedValues)
{
// Apply the constraint
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
public Func<string, IResolveConstraint> CreateConstraintFunction(ConstraintExpression constraintExpression, bool verifyExactSameValue)
{
if (verifyExactSameValue)
{
return (string value) => constraintExpression.EqualTo(value);
}
else
{
return (string value) => constraintExpression.Contains(value);
}
}
}
Sample code:
Verifiers verifiers = new Verifiers();
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
// This passes
foreach(string expectedValue in expectedValues)
{
Assert.That(collectedValues, Has.Exactly(1).Contains(expectedValue));
}
// This fails with the message: "Expected: collection containing "value2"" (with NUnit v3.2.1) / "Expected: some item equal to "value2""(with NUnit v3.12.0)
verifiers.VerifyCollectedValues(collectedValues, expectedValues, 1, false);
My assumption is that IResolveConstraint is causing the problem. What I don't understand is why the first item of list passes but the second one doesn't.
I'll be grateful for any answer.
UPDATE:
I've done some edits and base on that I excluded the possibility that IResolveConstraint is causing the problem. Here is sample of the current code:
public class Verifiers
{
public void VerifyCollectedValues(List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom, int countOfValueExpectance, bool verifyExactSameValue = true)
{
VerifyCollectedValues(CreateConstraintExpressionExactly(countOfValueExpectance), collectedValues, expectedValues, collectedValuesFrom, verifyExactSameValue);
}
private void VerifyCollectedValues(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom, bool exactSameValue)
{
if (exactSameValue)
{
VerifyCollectedValuesEqualTo(constraintExpression, collectedValues, expectedValues, collectedValuesFrom);
}
else
{
VerifyCollectedValuesContains(constraintExpression, collectedValues, expectedValues, collectedValuesFrom);
}
}
private void VerifyCollectedValuesEqualTo(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom)
{
Func<string, EqualConstraint> constraintFunction = (string value) => constraintExpression.EqualTo(value);
foreach (string expectedValue in expectedValues)
{
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
private void VerifyCollectedValuesContains(ConstraintExpression constraintExpression, List<string> collectedValues, List<string> expectedValues, string collectedValuesFrom)
{
// if I don't use constraintExpression but write the expression manually (Has.Exactly(1).Contains(value)), then it works just fine
Func<string, ContainsConstraint> constraintFunction = (string value) => constraintExpression.Contains(value);
foreach (string expectedValue in expectedValues)
{
Assert.That(collectedValues, constraintFunction(expectedValue));
}
}
private ConstraintExpression CreateConstraintExpressionExactly(int countOfExpectance)
{
return Has.Exactly(countOfExpectance);
}
var verifiers = new Verifiers();
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
// This one pass successfully
foreach(string expectedValue in expectedValues)
{
Assert.That(collectedValues, Has.Exactly(1).Contains(expectedValue));
}
// But this one still doesn't pass and fails at the second list item "value2"
verifiers.VerifyCollectedValues(collectedValues, expectedValues, "list of strings", 1, false);
Your assumption that Has.Exactly(1).EqualTo() will pass if the actual value is a non-collection is not correct. ExactCountConstraint will error (not quite the same as fail) if passed a non-collection type.
However, in this case, string is a collection of chars so it tries to run.
The problem is that you are using a collection constraint, which iterates the list, but you are also iterating it yourself. You should either do it all yourself (which seems silly) or rely on the ExactCountConstraint to do the work.
If you continue to iterate the list yourself, then you should not use any collection constraints. In the example case, you would simply use Is.EqualTo().
Well, the bug is in your implementation actually, since you're passing ConstraintExpression all the time.
If you try to decompose the working code and create methods for each verification you'll have something like this:
[Test]
public void ListCompareTest()
{
List<string> expectedValues = new List<string>()
{
"value1",
"value2",
"value3",
};
var collectedValues = new List<string>()
{
"some_value0",
"some_value1",
"some_value2",
"some_value3",
};
int count = 1;
// This one pass successfully
foreach (string expectedValue in expectedValues)
{
ItemsConstraintExpression itemsConstraintExpression = GetExactly(count);
// this code works
Assert.That(collectedValues, GetContains(expectedValue, itemsConstraintExpression));
}
// this code works as well
DoVerification(expectedValues, collectedValues, 1, false);
}
public static void DoVerification(List<string> expectedList, List<string> actualList, int exactlyCount, bool equalsOrContains)
{
if (equalsOrContains)
{
foreach (var expectedValue in expectedList)
{
Assert.That(actualList, GetEquals(expectedValue, GetExactly(exactlyCount)));
}
}
else
{
foreach (var expectedValue in expectedList)
{
Assert.That(actualList, GetContains(expectedValue, GetExactly(exactlyCount)));
}
}
}
private static EqualConstraint GetEquals(string expectedValue, ItemsConstraintExpression itemsConstraintExpression)
{
return itemsConstraintExpression.EqualTo(expectedValue);
}
private static ContainsConstraint GetContains(string expectedValue, ItemsConstraintExpression itemsConstraintExpression)
{
return itemsConstraintExpression.Contains(expectedValue);
}
private static ItemsConstraintExpression GetExactly(int count)
{
return Has.Exactly(count);
}

C# custom add in a List

I have the following list of strings :
var files = new List<string> {"file0","file1","file2","file3" };
I would like to be able to add new files to this list, but if the inserted file is present in the list, I would like to insert custom value that will respect the following format $"{StringToBeInserted}"("{SomeCounter}
For instance : try to add "file0" and "file0" is already I would like to insert "file0(1)". If I try again to add "file0" ... I would like to insert with "file0(2)" and so on ... Also, I would like to provide a consistency, for instance if I delete "file0(1)" ... and try to add again "item0" ... I expect that "item0(1)" to be added. Can someone help me with a generic algorithm ?
I would use a HashSet<string> in this case:
var files = new HashSet<string> { "file0", "file1", "file2", "file3" };
string originalFile = "file0";
string file = originalFile;
int counter = 0;
while (!files.Add(file))
{
file = $"{originalFile}({++counter})";
}
If you have to use a list and the result should also be one, you can still use my set approach. Just initialize it with your list and the result list you'll get with files.ToList().
Well, you should create your own custom class for it, using the data structure you described and a simple class that includes a counter and an output method.
void Main()
{
var items = new ItemCountList();
items.AddItem("item0");
items.AddItem("item1");
items.AddItem("item2");
items.AddItem("item0");
items.ShowItems();
}
public class ItemCountList {
private List<SimpleItem> itemList;
public ItemCountList() {
itemList = new List<SimpleItem>();
}
public void DeleteItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null) {
item.Count--;
if (item.Count == 0)
itemList.Remove(item);
}
}
public void AddItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null)
item.Count++;
else
itemList.Add(new SimpleItem {
Value = value,
Count = 1
});
}
public void ShowItems() {
foreach (var a in itemList) {
Console.WriteLine(a.Value + "(" + a.Count + ")");
}
}
}
public class SimpleItem {
public int Count {get; set;}
public string Value {get; set;}
}

Using reflection in ToString method [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm trying to clean up my class using reflection.
private List<String> centers = new List<String>();
private List<String> leftWingers = new List<String>();
private List<String> rightWingers = new List<String>();
private List<String> defencemen = new List<String>();
private List<String> goalies = new List<String>();
private List<String> bench = new List<String>();
public List<String> Centers { get { return centers; } set { centers = value; } }
public List<String> LeftWingers { get { return leftWingers; } set { leftWingers = value; } }
public List<String> RightWingers { get { return rightWingers; } set { rightWingers = value; } }
public List<String> Defencemen { get { return defencemen; } set { defencemen = value; } }
public List<String> Goalies { get { return goalies; } set { goalies = value; } }
public List<String> Bench { get { return bench; } set { bench = value; } }
public String ToString()
{
String output = "";
System.Reflection.PropertyInfo[] properties = this.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo property in properties)
{
int count = 0;
foreach (String value in property)
{
count++;
output += "C" + count + ": " + value + System.Environment.NewLine;
}
}
}
This will not work. I cant seem to be able to loop over items of each property because they are collections. Is there a way to get the contents of the String Lists from the PropertyInfo object?
They are not properties, they are fields,so you need to use GetFields
You need to tell GetFields method that you want to get private
members using BindingFlags, otherwise it will look for public and instance members by default.
var fields = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
Well, if you really want properties, the first thing is that you do not have properties on your code. You have just some attributes. Change it for properties if it is the case, for sample:
private List<String> centers { get; set; }
private List<String> leftWingers { get; set; }
private List<String> rightWingers { get; set; }
private List<String> defencemen { get; set; }
private List<String> goalies { get; set; }
private List<String> bench { get; set; }
Then you could read them as collections and read values, for sample:
public String ToString()
{
StringBuilder output = new StringBuilder();
System.Reflection.PropertyInfo[] properties = this.GetType().GetProperties();
foreach (System.Reflection.PropertyInfo property in properties)
{
var values = property.GetValue(this, null) as IEnumerable<String>;
if (values != null)
{
int count = 0;
foreach (String value in values)
{
count++;
output.AppendLine(string.Format("C{0}: {1}", count, value));
}
}
}
return output.ToString();
}
Another case is to read the fields and avoid converting it to properties. Look the Selman22's answer!
Your problem can be drastically simplified using Enumerable.Concat, Enumerable.Select and string.Join:
IEnumerable<string> allItems = centers.Concat(leftWingers)
.Concat(rightWingers)
.Concat(defencemen)
.Concat(goalies)
.Concat(bench);
return string.Join
(
Environment.NewLine,
allItems.Select((item, index) => $"C {index + 1}: {item}")
);
These are fields, so you need to use GetFields to obtain them. Please note also that fields returned by reflection are just metadata, they don't contain the data that you need.
To get the data, you need to use the GetValue method to get the field value for the current object.
Then, to be able to enumerate the collection in the field, you need to cast it as IEnumerable<string>.
Here is how it would look like:
public String ToString()
{
String output = "";
var fields = this.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
int count = 0;
foreach (String value in (IEnumerable<string>) field.GetValue(this))
{
count++;
output += "C" + count + ": " + value + System.Environment.NewLine;
}
}
return output;
}
Since you have only 6 collections, you can have a solution that does not involve reflection. Here is an example:
public String ToString()
{
String output = "";
var collections = new[] {centers, leftWingers, rightWingers, defencemen, goalies, bench};
foreach (var field in collections)
{
int count = 0;
foreach (String value in field)
{
count++;
output += "C" + count + ": " + value + System.Environment.NewLine;
}
}
return output;
}
Please consider using a StringBuilder instead of a string. Using a string to concatenate strings will hurt performance.

Populate a tree from a List of string arrays

I have list of arrays:
List<HeaderItem> _headerItems = new List<HeaderItem>();
class HeaderItem
{
private string[] _headers = new string[6];
public string this[int index]
{
get
{
return _headers[index];
}
set
{
_headers[index] = value;
}
}
}
Each of the 6 items in the array represent a level in the hierarchy. If all items matched in array position 0 then a single root level node would exist.
So,
A,B,C
A,B,D
B,C,D
B,D,E
would produce:
A
....B
........C
........D
B
....C
........D
....D
........E
etc....
Currently my solution is pretty hacked up and although it works I am trying to come up with a "cool" way of doing it.
You can achieve that by calling a print method recursively providing it with the subset of items to be printed and depth of the tree printed so far. I amended your class to contain a Length property so that the caller does not have to always assume it is 6. Also I added a constructor to make my initialization easy. Probably it wouldn't make sense in your code.
public class HeaderItem
{
public HeaderItem(string headers)
{
_headers = headers.ToCharArray().Select(x => x.ToString()).ToArray();
}
private string[] _headers = new string[6];
public int Length
{
get { return _headers.Length; }
}
//...
}
This is the print method. See how it does grouping and then calls itself recursively:
private static void PrintHeaders(IEnumerable<HeaderItem> headerItems, int depth = 0)
{
var result =
headerItems.Where(h => h.Length > depth)
.GroupBy(h => h[depth], h => h,
(k, g) => new {Key = k, Items = g})
.OrderBy(g => g.Key);
foreach (var pair in result)
{
Console.Write(new string('.', depth)); // change here to add more dots
Console.WriteLine(pair.Key);
PrintHeaders(pair.Items, depth + 1);
}
}
And this is how you can begin calling it:
PrintHeaders(_headerItems);
For testing, this is my Main method:
static void Main(string[] args)
{
_headerItems.Add(new HeaderItem("abc"));
_headerItems.Add(new HeaderItem("abd"));
_headerItems.Add(new HeaderItem("acd"));
_headerItems.Add(new HeaderItem("ace"));
_headerItems.Add(new HeaderItem("bce"));
_headerItems.Add(new HeaderItem("bcd"));
_headerItems.Add(new HeaderItem("bef"));
PrintHeaders(_headerItems);
Console.ReadLine();
}
And this is the result:
a
.b
..c
..d
.c
..d
..e
b
.c
..d
..e
.e
..f
When ever making tree always create a list of entity within that entity like this
class HeaderItem
{
private string[] _headers = new string[6];
private List<HeaderItem> _items;
public string this[int index]
{
get
{
return _headers[index];
}
set
{
_headers[index] = value;
}
}
public List<HeaderItem> Items
{
get
{
if (_items == null)
_items = new List<HeaderItem>();
return _items;
}
}
}

Need C# Class method that persists information already in Object

I have created a class in my ASP.Net project called LitHoldModifications. Here's the code:
[Serializable]
public class LitHoldModifications
{
private Boolean _changed;
private Hashtable _added;
private Hashtable _deleted;
public Boolean Changed
{
get { return _changed; }
set { _changed = value; }
}
public Hashtable Added
{
get { return _added; }
set { _added = value; }
}
public Hashtable Deleted
{
get { return _deleted; }
set { _deleted = value; }
}
public Hashtable Add(String item1, String item2)
{
Added = new Hashtable();
Added.Add(item1, item2);
return Added;
}
public Hashtable Delete(String item1, String item2)
{
Deleted = new Hashtable();
Deleted.Add(item1, item2);
return Deleted;
}
}
The problem I'm having is that I need to be able to Add multiple items to an instance of this class. The code I have to do this is (in an aspx page):
public LitHoldModifications AffectedEmployeeModifications
{
get
{
if (ViewState["AffectedEmployeeModifications"] != null)
return (LitHoldModifications)ViewState["AffectedEmployeeModifications"];
else
return null;
}
set
{
ViewState["AffectedEmployeeModifications"] = value;
}
}
protected void ProcessAffectedviaJavascript()
{
string[] employees = HiddenEmployeesPopup.Value.Split('|');
if (employees.Length>1) {
foreach (string s in employees)
{
if (s.Length > 1)
{
string Anumber = s.Split('#')[0];
string AName = s.Split('#')[1];
ListItem item = new ListItem();
item.Text = AName;
item.Value = Anumber;
lstSelEmployees.Items.Add(item);
//Clear values in temp hidden field:
HiddenEmployeesPopup.Value = "";
AffectedEmployeeModifications.Add(Anumber, AName);
AffectedEmployeeModifications.Changed = true;
}
}
}
When I run my code and get to ProcessAffectedviaJavascript(), the string[] employees is populated with multiple names, but each time the code gets to the line AffectedEmployeeModifications.Add.... a new Hashtable is created and returned by the Add method, so any earlier strings from employees that have been added to AffectedEmployeeModifications are lost. The only way I can think to get around this is to change the Add method to take AffectedEmployeeModifications as a parameter and do this:
public Hashtable Add(Hashtable lhm, String item1, String item2)
{
lhm.Add(item1, item2);
return lhm;
}
and then, in my aspx.cs:
AffectedEmployeeModifications = AffectedEmployeeModifications.Add(AffectedEmployeeModifications, Anumber, AName);
This doesn't seem very OOP-y though, and my OOP skills are clearly wanting. How should I do this?
You should be constructing the objects that your class needs in a constructor for the class. You should add a default constructor like this:
public LitHoldModifications()
{
Added = new Hashtable();
Deleted = new Hashtable();
}
Then you can remove the assignments in your Add() and Delete() methods, because you can assume in these methods that those member variables will already be valid.
I would also recommend changing your Add/Delete methods to returning void. If you want access to the Added/Deleted hashtables, you can just reference that property instead.
var myLitInstance = new LitHoldModifications();
myLitInstance.Add("value1", "value2");
Hashtable tbl = myLitInstance.Added;

Categories