How to compare sets that are nested in dictionaries with NUnit? - c#

In newer versions of NUnit, it is apparently possible to compare nested collections, as this answer said. However, it seems like this does not work so well with nested HashSets. Consider:
var a1 = new Dictionary<string, HashSet<string>> {
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
{ "c", new() { } },
};
var a2 = new Dictionary<string, HashSet<string>> {
{ "c", new() { } },
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
};
CollectionAssert.AreEqual(a1, a2);
This passes, but if I change the order in which I add values to the sets a little bit (or do anything that changes the iteration order of one of the sets):
var a1 = new Dictionary<string, HashSet<string>> {
{ "a", new() { "b", "c" } },
{ "b", new() { "c" } },
{ "c", new() { } },
};
var a2 = new Dictionary<string, HashSet<string>> {
{ "c", new() { } },
{ "a", new() { "c", "b" } },
{ "b", new() { "c" } },
};
CollectionAssert.AreEqual(a1, a2);
I expected it to still pass because a HashSet is unordered, but the test fails:
----> NUnit.Framework.AssertionException : Expected and actual are both <System.Collections.Generic.Dictionary`2[System.String,System.Collections.Generic.HashSet`1[System.String]]> with 3 elements
Values differ at index [0]
String lengths are both 1. Strings differ at index 0.
Expected: "b"
But was: "c"
-----------^
It seems like NUnit is not aware that HashSet is unordered, but is aware that a Dictionary is unordered.
So I think I need to use HashSet<string>.CreateSetComparer() somewhere. I know I can specify the equality comparer to use for a comparison with Using:
Assert.That(a, Is.EqualTo(b).Using(someEqualityComparer));
But I am trying to provide it for the comparison of the nested sets. How would I do that?
To avoid being an XY problem: The dictionary in my real code actually represents the edges of a control flow graph. It's actually a Dictionary<BasicBlock, HashSet<BasicBlock>>.

NUnit isn't exactly "aware" that dictionaries are unordered. However, it does have special code for testing Dictionary equality, as described on this page. You'll note that the page doesn't say anything about Hashsets, which get no special treatment.
As you suggested, the simplest way to deal with Hashsets in your example is to define your own comparer. I suggest defining one, which implements, IEqualityComparer<Hashset>. This will automatically take care of the nesting problem, since NUnit will only use that comparer when it reaches the nested Hashsets.
If necessary, you can repeat Using to create multiple comparers for different types. However, from what you have said, that doesn't seem to be necessary in this case.

Related

Can I pass arguments to a base constructor from a derived class's default constructor?

Suppose I have an abstract base class Deck:
public abstract class Deck
{
public List<Card> cards;
public Deck(string[] values, string[] suits)
{...}
...
}
and a derived class EuchreDeck:
public class EuchreDeck : Deck
{
string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public EuchreDeck() : base(values, suits) // Error.
{}
...
}
I want the ability to instantiate EuchreDeck and have the two string arrays passed to the base class, i.e. var gameDeck = new EuchreDeck();.
Currently I'm getting the error: "An object reference is required for the non-static field, method, or property EuchreDeck.values."
Is this possible, or will calling the derived default constructor always call the base default constructor?
Yes, you can do this if you make the arrays static:
public class EuchreDeck : Deck
{
private static readonly string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
private static readonly string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public EuchreDeck() : base(values, suits)
{
}
}
The reason why you can't use it as you had with instance-level members is because it's not legal to do so. This comes from the C# specification 10.10.1 Constructor Initializers where it states:
An instance constructor initializer cannot access the instance being
created. Therefore it is a compile-time error to reference this in an
argument expression of the constructor initializer, as is it a
compile-time error for an argument expression to reference any
instance member through a simple-name.
By switching the arrays to be static, they are no longer accessed via the instance but rather by the EuchreDeck type.
That said, I might suggest you take a slight tweak on the design. Maybe use a factory to create these specialized decks for you rather than their constructors.
As an example, maybe refactor something like this:
Change your base Deck to just take the set of cards:
public abstract class Deck
{
public List<Card> Cards;
protected Deck(IEnumerable<Card> cards)
{
this.Cards = new List<Card>(cards);
}
}
Then have the factory setup like this:
public class EuchreDeck : Deck
{
private EuchreDeck(IEnumerable<Card> cards) : base(cards)
{
}
public class Factory : DeckFactory
{
private static readonly string[] Values = new string[] { "9", "10", "J", "Q", "K", "A" };
private static readonly string[] Suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
public static EuchreDeck Create()
{
var cards = CreateCards(Values, Suits);
return new EuchreDeck(cards);
}
}
}
Instantiation/usage as:
EuchreDeck.Factory.Create();
You could play around with the factory usage. I just nested it in the class so you couldn't create a EuchreDeck with an invalid set of cards. Your DeckFactory base would have your conversion method (which looks like you currently have in your Deck constructor)
Beyond that, I'm not sure if you have a specific need for a EuchreDeck; I'm assuming you have other methods associated with it? If not, you could probably ditch the class altogether and just let the factory create a Deck with the needed cards.
I think the problem is that the values and suits should be declared static.
static string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
static string[] suits = new string[] { "clubs", "spades", "hearts", "diamonds" };
That way, they will be available at when instantiating the new class.
You have to have somewhere that variable decalred :
public EuchreDeck() : base(values, suits) // Error.
{}
where are values and suits declared?
You have to define them static, like:
static readonly string[] values = new string[] { "9", "10", "J", "Q", "K", "A" };
static readonly string[] suits = new string[]
{ "clubs", "spades", "hearts", "diamonds" };
The thing is that base(..) can only access values visible in ctor scope. So you can not pass instance variables, as instance is not accessible on that level, but you can pass ctor parameters and static members of calling class.

Setting jagged array values within a class

I'm having trouble understanding this problem with creating a jagged array in C#. If you look at the code below, it compiles fine. But if I were to take the 2 lines that are assigning values to the array "places" and move them from within the method to within the class itself, the compiler starts complaining with a lot of strange errors.
At first I thought that its because of the usage of the 'new' keyword within a class (a class being just a definition whereas 'new' refers to an instance... you can't have an instance within a definition, can you?). But then I noticed the usage of the 'new' keyword in the initialization of "places" was OK even though it was initialized within the class. Please explain.
public class Place
{
string[][] places = new string[2][];
public void enumerate()
{
places[0] = new string[] { "Canada", "United States" };
places[1] = new string[] { "Calgary", "Edmonton", "Toronto" };
Console.WriteLine(places[0][1]);
}
}
EDIT: to be explicit, the error is produced when running
public class Place
{
string[][] places = new string[2][];
places[0] = new string[] { "Canada", "United States" };
places[1] = new string[] { "Calgary", "Edmonton", "Toronto" };
public void enumerate()
{
Console.WriteLine(places[0][1]);
}
}
errors received "array size cannot be specified in a variable declaration (try initializing with a "new" expression)" "invalid tokens =, {, in class" "namespace cannot directly contain members such as fields or methods"
Of course you can initialize an jagged array, but the syntax is slightly different:
public class Place
{
private string[][] places = new string[2][]
{
new string[] { "Canada", "United States" },
new string[] { "Calgary", "Edmonton", "Toronto" },
};
public void enumerate()
{
Console.WriteLine(places[0][1]);
}
}

Alternating params

Is there a kind of alternating params for method parameters?
I like the keyword params. But sometimes I need two parameters to be params.
I want to call a method like so:
Method(1, "a", 2, "b", 3, "c")
where 1, 2 and 3 are keys and "a", "b" and "c" are assigned values.
If I try to define the method parameters I would intuitively try to use params for two parameters like so:
void Method(params int[] i, string[] s)
Compiler would add every parameter at odd positions to the first parameter and every parameter at even positions to the second parameter.
But (as you know) params is only possible for last parameter.
Of course I could create a parameter class (e.g. KeyValue) and use it so:
Method(new[] {new KeyValue(1, "a"), new KeyValue(2, "b"), new KeyValue(3, "c")})
But that is too much code imo.
Is there any shorter notation?
Edit: Just now I found a good answer to another question: It suggests to inherit from List and to overload the Add method so that the new List can be initialized by this way:
new KeyValueList<int, string>{{ 1, "a" }, { 2, "b" }, { 3, "c" }}
Method definition would be:
void Method(KeyValueList<int, string> list)
Call would be:
Method(new KeyValueList<int, string>{{ 1, "a" }, { 2, "b" }, { 3, "c" }})
There is no "alternating params" notation as you described.
You can only have one params parameter and it must be last - if you want to have different types as params parameters you can use object as the array type.
Consider passing in a list made of a custom type that retains the meaning of these items.
public class MyType
{
public int MyNum { get; set; }
public string MyStr { get; set; }
}
Method(List<MyType> myList);
You could do this via params object[] keysAndValues and sort it out yourself, but... its a bit icky, what with all the boxing/unboxing that would go on.
Just giving an updated answer for today's developers looking for a solution...
Old School Answer:
You could create a class or struct that includes the parameters of the type you need...
struct Struct {
public int Integer {get; set;}
public string Text {get; set;}
public Struct(int integer, string text) {
Integer = integer;
Text = text;
}
}
... and then define your function to accept it as the params array...
void Method(params Struct[] structs)
... and call it like...
Method(new Struct(1, "A"), new Struct(2, "B"), new Struct(3, "C"));
New School Answer:
Or, with the latest C#, you could simply use ValueTuples...
Method(params (int, string)[] valueTuples)
... and call it like...
Method((1, "A"), (2, "B"), (3, "C"))

Collection Initializers in C#

In Java, I can create an List and immediately populate it using a static initializer. Something like this:
List &ltString&gt list = new ArrayList&ltString&gt()
{{
Add("a");
Add("b");
Add("c");
}}
Which is convenient, because I can create the list on the fly, and pass it as an argument into a function. Something like this:
printList(new ArrayList&ltString&gt()
{{
Add("a");
Add("b");
Add("c");
}});
I am new to C# and trying to figure out how to do this, but am coming up empty. Is this possible in C#? And if so, how can it be done?
You can use a collection initializer:
new List<string> { "a", "b", "c" }
This compiles to a sequence of calls to the Add method.
If the Add method takes multiple arguments (eg, a dictionary), you'll need to wrap each call in a separate pair of braces:
new Dictionary<string, Exception> {
{ "a", new InvalidProgramException() },
{ "b", null },
{ "c", new BadImageFormatException() }
}
Since C# 3.0 you can do it as well:
List <String> list = new List<String>
{
"a", "b", "c"
};
MSDN, Collection Initializers
Collection initializers let you specify one or more element
intializers when you initialize a collection class that implements
IEnumerable. The element initializers can be a simple value, an
expression or an object initializer. By using a collection initializer
you do not have to specify multiple calls to the Add method of the
class in your source code; the compiler adds the calls.
EDIT: Answer to comment regarding dictionary
IDictionary<string, string> map = new Dictionary<string, string>
{
{ "Key0", "Value0" },
{ "Key1", "Value1" }
};
Yes, it's described on MSDN here

Using key-value pairs as parameters

Simple. If I use:
public void Add(params int[] values)
Then I can use this as:
Add(1, 2, 3, 4);
But now I'm dealing with key-value pairs! I have a KeyValue class to link an integer to a string value. So I start with:
public void Add(params KeyValue[] values)
But I can't use this:
Add(1, "A", 2, "B", 3, "C", 4, "D");
Instead, I'm forced to use:
Add(new KeyValue(1, "A"), new KeyValue(2, "B"), new KeyValue(3, "C"), new KeyValue(4, "D"));
Ewww... Already I dislike this...
So, right now I use the Add function without the params modifier and just pass a pre-defined array to this function. Since it's just used for a quick initialization for a test, I'm not too much troubled about needing this additional code, although I want to keep the code simple to read. I would love to know a trick to use the method I can't use but is there any way to do this without using the "new KeyValue()" construction?
If you accepted an IDictionary<int,string>, you could presumably use (in C# 3.0, at least):
Add(new Dictionary<int,string> {
{1, "A"}, {2, "B"}, {3, "C"}, {4, "D"}
});
Any use?
Example Add:
static void Add(IDictionary<int, string> data) {
foreach (var pair in data) {
Console.WriteLine(pair.Key + " = " + pair.Value);
}
}
You can modify your current class design, but you will need to add generics and use the IEnumerable interface.
class KeyValue<TKey, TValue>
{
public KeyValue()
{
}
}
// 1. change: need to implement IEnumerable interface
class KeyValueList<TKey, TValue> : IEnumerable<TKey>
{
// 2. prerequisite: parameterless constructor needed
public KeyValueList()
{
// ...
}
// 3. need Add method to take advantage of
// so called "collection initializers"
public void Add(TKey key, TValue value)
{
// here you will need to initalize the
// KeyValue object and add it
}
// need to implement IEnumerable<TKey> here!
}
After these additions you can do the following:
new KeyValueList<int, string>() { { 1, "A" }, { 2, "B" } };
The compiler will use the IEnumerable interface and the Add method to populate the KeyValueList. Note that it works for C# 3.0.
If you are using this for tests, these changes are not worth it. It's quite an effort and you change quite a lot of production code for tests.
You could use something like the following with the obvious drawback that you loose strong typing.
public void Add(params Object[] inputs)
{
Int32 numberPairs = inputs.Length / 2;
KeyValue[] keyValues = new KeyValue[numberPairs];
for (Int32 i = 0; i < numberPairs; i++)
{
Int32 key = (Int32)inputs[2 * i];
String value = (String)inputs[2 * i + 1];
keyvalues[i] = new KeyValue(key, value);
}
// Call the overloaded method accepting KeyValue[].
this.Add(keyValues);
}
public void Add(params KeyValue[] values)
{
// Do work here.
}
You should of cause add some error handling if the arguments are of incorrect type. Not that smart, but it will work.

Categories

Resources