Elegant initialization of an array of class instances in C# [closed] - c#

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
Let's say I have a class like this:
public class Fraction
{
int numerator;
int denominator;
public Fraction(int n, int d)
{
// set the member variables
}
// And then a bunch of other methods
}
I want to initialize an array of them in a nice way, and this post is a big list of approaches that are error prone or syntactically cumbersome.
Of course an array constructor would be nice, but there's no such thing:
public Fraction[](params int[] numbers)
So I'm forced to use a method like
public static Fraction[] CreateArray(params int[] numbers)
{
// Make an array and pull pairs of numbers for constructor calls
}
which is relatively clunky, but I don't see a way around it.
Both forms are error prone because a user could mistakenly pass an odd number of parameters, maybe because s/he skipped a value, which would leave the function scratching its head wondering what the user actually wanted. It could throw an exception, but then the user would need to try/catch. I'd rather not impose that on the user if possible. So let's enforce pairs.
public static Fraction[] CreateArray(params int[2][] pairs)
But you can't call this CreateArray in a nice way, like
Fraction.CreateArray({0,1}, {1,2}, {1,3}, {1,7}, {1,42});
You can't even do
public static Fraction[] CreateArray(int[2][] pairs)
// Then later...
int[2][] = {{0,1}, {1,2}, {1,3}, {1,7}, {1,42}};
Fraction.CreateArray(numDenArray);
Note that this would work just fine in C++ (I'm pretty sure).
You're forced to do one of the following instead, which is abhorrent. The syntax is terrible and it seems really awkward to use a jagged array when all the elements have the same length.
int[2][] fracArray = {new int[2]{0,1}, /*etc*/);
Fraction.CreateArray(fracArray);
// OR
Fraction.CreateArray(new int[2]{0,1}, /*etc*/);
Similarly, Python-style tuples are illegal and the C# version is icky:
Fraction.CreateArray(new Tuple<int,int>(0,1), /*etc*/);
The use of a pure 2D array might take the following form, but it's illegal, and I'm sure there's no legal way to express it:
public static Fraction[] CreateArray(int[2,] twoByXArray)
// Then later...
Fraction[] fracArray =
Fraction.CreateArray(new int[2,4]{{0,1}, {1,2}, {1,3}, {1,6}});
This doesn't enforce pairs:
public static Fraction[] CreateArray(int[,] twoByXArray)
OK, how about
public static Fraction[] CreateArray(int[] numerators, int[] denominators)
But then the two arrays might have different lengths. C++ allows
public static Fraction[] CreateArray<int N>(int[N] numerators, int[N] denominators)
but, well, this isn't C++, is it?
This sort of thing is illegal:
public static implicit operator Fraction[](params int[2][] pairs)
and unworkable anyway, again because of the abhorrent syntax:
Fraction[] fracArray = new Fraction[](new int[2]{0,1}, /*etc*/ );
This might be nice:
public static implicit operator Fraction(string s)
{
// Parse the string into numerator and denominator with
// delimiter '/'
}
Then you can do
string[] fracStrings = new string[] {"0/1", /*etc*/};
Fraction[] fracArray = new Fraction[fracStrings.Length];
int index = 0;
foreach (string fracString in fracStrings) {
fracArray[index] = fracStrings[index];
}
I don't like this approach for five reasons. One, the implicit cast unavoidably instantiates a new object, but we already have a perfectly good one, namely the one we're trying to initialize. Two, it can be confusing to read. Three, it forces you to do explicitly what I wanted to encapsulate in the first place. Four, it leaves room for bad formatting. Five, it involves one-time parsing of string literals, which is more like a practical joke than good programming style.
The following also requires wasteful instantiation:
var fracArray = Array.ConvertAll(numDenArray, item => (Fraction)item);
The following use of a property has the same problem unless you use those terrible jagged arrays:
public int[2] pair {
set {
numerator = value[0];
denominator = value[1];
}
}
// Then later...
var fracStrings = new int[2,4] {{0,1}, /*etc*/};
var fracArray = new Fraction[fracStrings.Length];
int index = 0;
foreach (int[2,] fracString in fracStrings) {
fracArray[index].pair = fracStrings[index];
}
This variation doesn't enforce pairs:
foreach (int[,] fracString in fracStrings) {
fracArray[index].pair = fracStrings[index];
}
Again, this approach is big anyway.
These are all of the ideas I know how to derive. Is there a good solution?

I can't think of an elegant, and at the same time memory efficient solution for array.
But there is an elegant solution for list (and similar) utilizing the C# 6 collection initializer feature:
public static class Extensions
{
public static void Add(this ICollection<Fraction> target, int numerator, int denominator)
{
target.Add(new Fraction(numerator, denominator));
}
}
With that extension method in place, you can easily initialize a Fraction list for instance:
var list = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } };
And of course, although not memory efficient, you can use it to initialize Fraction array either:
var array = new List<Fraction> { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } }.ToArray();
or even making it more concise by declaring a list derived class with implicit array conversion operator:
public class FractionList : List<Fraction>
{
public static implicit operator Fraction[](FractionList x) => x?.ToArray();
}
and then use
Fraction[] array = new FractionList { { 0, 1 }, { 1, 2 }, { 1, 3 }, { 1, 7 }, { 1, 42 } };

You could create a fraction array builder with a fluent interface. It would lead to something like
public class FractionArrayBuilder
{
private readonly List<Fraction> _fractions = new List<Fraction>();
public FractionArrayBuilder Add(int n, int d)
{
_fractions.Add(new Fraction(n, d));
return this;
}
public Fraction[] Build()
{
return _fractions.ToArray();
}
}
which can be called using
var fractionArray = new FractionArrayBuilder()
.Add(1,2)
.Add(3,4)
.Add(3,1)
.Build();
which is an easy to understand statement.
I have made a fiddle to demonstrate.

The most succinct way I can think of for your particular example involves writing an implicit operator for the Fraction class:
public sealed class Fraction
{
public Fraction(int n, int d)
{
Numerator = n;
Deniminator = d;
}
public int Numerator { get; }
public int Deniminator { get; }
public static implicit operator Fraction(int[] data)
{
return new Fraction(data[0], data[1]);
}
}
Then you can initialise it like this:
var fractions = new Fraction[]
{
new [] {1, 2},
new [] {3, 4},
new [] {5, 6}
};
Unfortunately you still need the new [] on each line, so I don't think this gains very much over the normal array initialisation syntax:
var fractions = new []
{
new Fraction(1, 2),
new Fraction(3, 4),
new Fraction(5, 6)
};
I suppose you could write a "local" Func<> with a short name to simplify the initialisation somewhat:
Func<int, int, Fraction> f = (x, y) => new Fraction(x, y);
var fractions = new []
{
f(1, 2),
f(3, 4),
f(5, 6)
};
The drawback is that you'd need to add that extra line (initialising a Func<>) wherever you wanted to initialise the array - or have a private static method in the class instead - but then that method would be in scope throughout the class, which isn't ideal if it has a single-letter name.
However, the advantage of this approach is that it is very flexible.
I toyed with the idea of calling the inline function _, but I'm really not sure about that...
Func<int, int, Fraction> _ = (x, y) => new Fraction(x, y);
var fractions = new []
{
_(1, 2),
_(3, 4),
_(5, 6)
};

Related

Using struct in dictionary as a value

Hi I have a struct and dictionary as below and I'm trying to add it as a custom value
like
public struct data_inv
{
//protected static int p;
public float inventory;
public float supply;
public float demand;
};
public static IDictionary<int, data_inv> inv_stored = new Dictionary<int, data_inv>();
and I have tried to add value to dictionary but when I try to add a value like inv_stored[1].demand = 4;
its gives System.Collections.Generic.KeyNotFoundException: 'The given key was not present in the Dictionary.' exception. I'm new to coding, could any explain what im doing wrong
If you want to use a struct instead of a class, write this instead of inv_stored[1].demand = 4 (updated based on comments):
public struct data_inv
{
public float Inventory;
public float Supply;
public float Demand;
public data_inv(int demand)
{
Inventory = 0;
Supply = 0;
Demand = demand;
}
};
// ....
IDictionary<int, data_inv> inv_stored = new Dictionary<int, data_inv>();
data_inv myData = new data_inv(4);
inv_stored.Add(1, myData);
This is a way to add keyValue to a Dictionary.
data_inv shouldn't be a struct. It's mutable, and structs shouldn't be mutable, it's not representing a single value. You should make it a class instead.
public class data_inv
{
//protected static int p;
public float inventory;
public float supply;
public float demand;
};
Implementation:
// Add new item into dictionary
inv_stored.Add(1, new data_inv()
{
inventory = 20,
supply = 10,
demand = 5
});
Console.WriteLine(inv_stored[1].demand); // 5
inv_stored[1].demand = 4;
Console.WriteLine(inv_stored[1].demand); // 4
Edit:
Print all elements each in one line:
foreach (var kvp in inv_stored)
{
Console.Write("bucket:{0} ", kvp.Key);
Console.Write("inventory:{0}, ", kvp.Value.inventory);
Console.Write("supply:{0}, ", kvp.Value.supply);
Console.WriteLine("demand:{0}", kvp.Value.demand);
}
Example output:
bucket:1 inventory:20, supply:10, demand:4
bucket:2 inventory:16, supply:9, demand:7
Print all elements into a table format:
var buckets = inv_stored.Keys;
var inventory = inv_stored.Values.Select(x => x.inventory);
var supply = inv_stored.Values.Select(x => x.supply);
var demand = inv_stored.Values.Select(x => x.demand);
Console.WriteLine("buckets:\t{0}", string.Join("\t", buckets));
Console.WriteLine("inventory:\t{0}", string.Join("\t", inventory));
Console.WriteLine("supply: \t{0}", string.Join("\t", supply));
Console.WriteLine("demand: \t{0}", string.Join("\t", demand));
Example Output:
buckets: 1 2 3
inventory: 20 16 56
supply: 10 9 44
demand: 4 7 23
There's multiple problems with your code.
First, you can create a new record in a dictionary by doing something like dict[key] = value;. However, you can not do dict[key].field = value;. The setter can be used with non-existent keys, the getter can't.
Second, while you can use structs as values in a dictionary, you cannot set their fields directly. Structs use value-type semantics by default (i.e. whenever you don't explicitly use ref or take a pointer), so any change you made this way would be done to a copy of the struct, not the actual value in the dictionary. You can use something like this:
var val = dict[key];
val.field = newFieldValue;
dict[key] = val;
Third, it's generally considered bad practice to use mutable structs, exactly because of these complications. Mutable structs only have place in highly optimised code and native interop, and they need to be carefully tested and monitored. Heck, even many automated refactorings can break code with mutable structs.

How can I get a sorted `List<string>` based on an associated numeric index

I want to sort a string using indexes, but it's not working after 10th index, 10th/later indexes added after 1st index in the list after using Sort() method.
I have tried below code, but it's not working.
List<string> stringList = new List<string>();
foreach (ManagementObject disk in objectSearcher.Get() )
{
stringList.Add(string.Format("{0, -15} {1,-35} {2, -20}",
disk.GetPropertyValue("Index"),
disk.GetPropertyValue("Model"),
diskSize));
}
stringList.Sort();
In the above scenario, the code is working fine for 0-9 indexes but for later indexes, this is not working as expected.
Put your object into a class structure and work with that strong type as long as possible:
public class DiskInfo
{
private int index = 0;
private string model = String.Empty;
private unsigned long size = 0;
public int getIndex() { return index; }
public string getModel() { return model; }
public unsigned long getSize() { return size; }
public DiskInfo(int index, string model, unsigned long size)
{
this.index = index;
this.model = model;
this.size = size;
}
public string ToString()
{
return string.Format("{0, -15} {1,-35} {2, -20}", index, model, size);
}
}
// ...
List<DiskInfo> lst = new List<DiskInfo>();
foreach (ManagementObject disk in objectSearcher.Get() )
{
lst.Add(new DiskInfo(
disk.GetPropertyValue("Index"),
disk.GetPropertyValue("Model"),
diskSize
));
}
Adjust types as needed.
Then you can use simple linq to sort.
lst = lst.OrderBy(x => x.getIndex());
On top of that you get IDE support and compiler errors instead of trying to figure out why you get format exceptions, etc when mucking around with strings.
If your input data is not of the correct data type, then cast it then and there.
For example, index gets passed as a string:
string strIdx = "15";
lst.Add(new DiskInfo(int.Parse(strIdx)), ...)
It's not working after 10th index.
That is because List().Sort invoke string's comparison function.In string comparison "0" is less than "1", "1" is less than "11" and "12" is less than "2" etc.So it is not working after 10.
You can definition a sample comparison function as below:
public static int Compare(string a, string b)
{
return int.Parse(a.Substring(0, 15)).CompareTo(int.Parse(b.Substring(0, 15)));
}
and then invoke it in sort method:
stringList.Sort(Compare);
The prerequisite is that your format is satisfied that its first 15 characters can convert to an integer.
You are probably looking for the "logical sort order" seen in Windows Explorer. Below I have replaced the default string comparer with a comparer using that API: StrCmpLogicalW
class Program
{
public sealed class NaturalStringComparer : IComparer<string>
{
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string psz1, string psz2);
public int Compare(string a, string b) => StrCmpLogicalW(a, b);
}
static void Main()
{
var stringList = new List<string>();
var index = 0;
while (index < 12)
{
stringList.Add($"{index,-15} {"Model",-35} {"35GB",-20}");
index++;
}
stringList.Sort(new NaturalStringComparer());
foreach (var s in stringList)
{
Console.WriteLine(s);
}
}
}
You seem to be deliberately left aligning index your numbers, which will mean that the ascending string sorted sequence of 1 through 12 would would be 1, 11, 12, 2, 3, 4, ...
Since you have the index value during the creation of the string, it would be wasteful to again parse the number out of the string in order to sort it. It would be better to retain the index and the string separately in a suitable data structure, sort by the index, and then project out just the string.
Updated for OP's new Question
Creating a custom POCO class (with or without an IComparable implementation) seems overkill everytime you need to sort an enumerable of related data by one of its properties.
Instead, you can easily build up a sortable anon class, struct or tuple containing the sortable integer and the concatenated string, then sort, then project out just the string. Either way, OP's GetPropertyValue method appears to return (reflect) a weak type such as object or string - accepted answer wouldn't compile as it needs to cast index to an int.
Here's value tuple solution:
var tuples = new List<(int index, string str)>();
foreach (ManagementObject disk in objectSearcher.Get() )
{
var indexValue = int.Parse(disk.GetPropertyValue("Index"));
tuples.Add((indexValue, string.Format("{0, -15} {1,-35} {2, -20}",
indexValue,
disk.GetPropertyValue("Model"),
diskSize)));
}
// Sort by index, and project out the assembled string.
var myList = tuples
.OrderBy(t => t.index)
.Select(t => t.str)
.ToList();
Original Answer, OP had a simple loop
What I've done below is to keep a Value tuple of the original string, and the parsed integer value of the first 15 digits.
Note that this will break if there are non-numeric characters in the first 15 characters of your string.
// Test Data
var strings = Enumerable.Range(0, 12)
.Select(i => (string.Format("{0, -15} {1,-35} {2, -20}", i, "Model", "35GB")));
// Project out a tuple of (index, string)
var indexedTuples = strings.Select(s => (idx: int.Parse(s.Substring(0, 15)), str: s));
var sorted = indexedTuples.OrderBy(t => t.idx)
.Select(t => t.str);

List<T>.RemoveAll() efficiency / compiler optimisation

Regarding efficiency, does anyone know if the compiler is clever enough to not create the array containing 1, 3, 5 for each iteration of the loop in the following code?
var foo = new List<int> { 1, 2, 3, 4, 5 };
foo.RemoveAll(i => new[] { 1, 3, 5 }.Contains(i));
I prefer it for readability, but not at the sake of performance.
The answer is no it doesn't optimize out the allocation of the array
Basically, every time the predicate is called, it checks against the compiler generated class and initializes a new array to call the Contains (as you can see here)
private sealed class <>c
{
public static readonly <>c <>9 = new <>c();
public static Predicate<int> <>9__0_0;
internal bool <M>b__0_0(int i)
{
// bam!
int[] obj = new int[3];
RuntimeHelpers.InitializeArray(obj, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
return Enumerable.Contains(obj, i);
}
}
As #Michael Randall already wrote, it looks like it is not possible.
I agree, that your questioned code is nicely readable, having the list in the RemoveAll method. But to have the instance only once, I have three ideas of doing it:
int[] a = null;
foo.RemoveAll(i => (a ?? (a = new[] { 1, 3, 5 })).Contains(i));
This is actually yours, with little un-beatuness of needing an external variable.
foo = foo.Except(new[] { 1, 3, 5 }).ToList();
That's actually pretty nice solution using Linq.
new List<int>{1, 3, 5}.ForEach(x => foo.Remove(x));
new[] {1, 3, 5}.Iterate(x => foo.Remove(x));
This is something I'd do. In neary all of my code I have my Extension method "Iterate" to avoid the need of foreach. And also, i dont want to "toList" everything all the time to make a .ForEach(..)
static class Extensions
{
public static void Iterate<TSource>(this IEnumerable<TSource> source, Action<TSource> action)
{
foreach (var item in source)
{
action.Invoke(item);
}
}
}
Since the compiler is not that smart, we must outsmart him.
var foo = new List<int> { 1, 2, 3, 4, 5 };
var bar = new HashSet<int>() { 1, 3, 5 };
foo.RemoveAll(i => bar.Contains(i));

Using IComparable to compare arrays

I have a problem with a school task.
"Let's say class List are constructors to generate random arrays.
Class List1 inherits from class List and uses IComparable to compare first elements, then second ones etc. (blank is a 0) {1,1,1}<{1,2}<{2}
Class List2 inherits likewise and compares sizes of arrays by size. If sizes are the same, check like in List1 {1,2,3}<{1,2,3,4}<{2,2,3,4}"
I made class List.
public List(int b)
{
tabb = new int[b];
for (int i = 0; i < b; i++)
tabb[i] = r1.Next(0, 100);
}
I noticed that inheriting from List to List1 the constructors are not inherited, so i dont know where and how to use IComparable
class List1 : List,IComparable<List1>
{
public int CompareTo(List1 x){
return this.CompareTo(x);
}
}
I noticed that inheriting from List to List1 the constructors are not inherited, so i dont know where and how to use IComparable
I can't comprehend that sentence as a single question, as the lack of constructors being inherited seems completely independent to me of how to use IComparable<T>.
So, let's address the two questions separately:
You are correct that constructors are not inherited. That is, while they still exist, they are callable only by the derived type, and not directly usable by outside code. If a base class does not have a parameterless constructor, then as the implementer of the derived class, it is your responsibility to provide a constructor and then call the appropriate base class constructor (every class will have some constructor). For example:
class List1 : List
{
public List1(int count) : base(count) { }
}
(I'm assuming here that List is some type other than the .NET List<T> type, i.e. it's not generic and is defined in your own context.)
In this way, you provide the necessary constructor, and ensure that the correct base constructor is called, by using : base(count) to pass the parameter value to your constructor.
How to use IComparable<T>. It seems to me that this is the crux of your assignment. I would be doing you a disservice to write the code for you. However, I'll try to elaborate on the instructions to help you understand what they seem to be asking (of course, your teacher is the best person for you to receive this advice from).
You have two different assignments here. Both require that you implement the interface IComparable<T>, so the first thing you need to do is make sure you understand what it means to implement an interface. Do you? An interface is a kind of "contract". In any interface declaration, there are described the members that an implementation of that interface is required to provide. If you look at the definition of IComparable<T>, you'll see it requires implementation of just one member: a CompareTo() method.
So, your assignment comes down to how to implement that method for each of the two required subclasses, List1 and List2. The basic idea of the implementation is the same for both, but the specifics are different.
In the first case, the comparison will order the two instances of List1 according to the contents of the collection where the order of those instances is determined by the ordering of the first non-equal element in the same position of the array in each instance. In the examples given, {1, 1, 1} is treated as "less than" {1, 2} because when you compare the elements at each position for each instance of List1, the first corresponding position where the elements are different is the second position (index 1), where {1, 1, 1} has the value 1 and {1, 2} has the value 2. The value 1 is less than the value 2, so the whole instance of {1, 1, 1} is "less than" the whole instance of {1, 2}. (Unfortunately, the assignment as described in your post is not clear on how to order instances where the underlying list of one is shorter than the underlying list of the other, but has exactly the same values in those element positions. I would make the shorter list be treated as "less than", but that's not unambiguously the only valid way to do it.)
In the second part of the assignment, implementing List2, the only thing that's being compared is the length of the list. This should be much easier to implement than the first part of the assignment, because you have only a single value to compare in each List2 instance, i.e. tabb.Length.
Note that in both cases, you can take advantage of the fact that int also implements IComparable<T>, so you can use its CompareTo() method to determine whether corresponding values in each instance of your class are less than, equal to, or greater than each other.
I hope that that's enough to get you pointed in the right direction. If you need more help than that, you probably should just consult with your instructor. They will know exactly how much help they want to provide to you, while remaining short of actually doing the assignment for you.
Sorry, I didn't mean to go all out on this. But then it just happened. It's late. It interested me, especially the need to be able to test it even though the requirement for random numbers makes it difficult to test.
The solution I came do was to separate the list from how it gets populated. That way for test purposes I can substitute a "populator" that inserts exactly the values I want. That way I can create the scenarios for testing.
// This is the base list - it just requires something to populate it.
public class IntegerList : List<int>
{
public IntegerList(IIntegerListPopulator populator, int size)
{
populator.PopulateList(this, size);
}
}
// Interface and implementation to populate a list with random numbers.
public interface IIntegerListPopulator
{
void PopulateList(List<int> target, int size);
}
public class RandomIntegerListPopulator : IIntegerListPopulator
{
public void PopulateList(List<int> target, int size)
{
var random = new Random();
for (var i = 0; i < size; i++)
{
target.Add(random.Next(0, 100));
}
}
}
// Compares by values, but the populator is injected - needed so that
// the class can be tested.
public class IntegerListThatComparesByValues : IntegerList, IComparable<IntegerListThatComparesByValues>
{
public IntegerListThatComparesByValues(IIntegerListPopulator populator, int size)
: base(populator, size)
{ }
public int CompareTo(IntegerListThatComparesByValues other)
{
return new IntegerListValueComparer().Compare(this, other);
}
}
// Class to perform comparisons by value. There's no real point
// in implementing IComparer since I'm not using it that way,
// but it doesn't hurt.
public class IntegerListValueComparer : IComparer<IntegerList>
{
public int Compare(IntegerList x, IntegerList y)
{
// I made this part up. I don't actually know how
// you want to handle nulls.
if (x == null && y == null) return 0;
if (x == null) return 1;
if (y == null) return -1;
// Always compare the longer one to the shorter.
// if this one is shorter, do the reverse comparison
// and reverse the result.
if (y.Count < x.Count) return -Compare(y, x);
if (x.SequenceEqual(y)) return 0;
for (var index = 0; index < x.Count; index++)
{
var comparison = x[index].CompareTo(y[index]);
if (comparison != 0) return comparison;
}
// If the other list is longer than this one, then assume
// that the next element of this list is 0.
return -y[x.Count];
}
}
public class IntegerListThatComparesByLength : IntegerList, IComparable<IntegerListThatComparesByLength>
{
public IntegerListThatComparesByLength(IIntegerListPopulator populator, int size)
: base(populator, size)
{
}
public int CompareTo(IntegerListThatComparesByLength other)
{
var comparisonByCount = Count.CompareTo(other?.Count ?? 0);
return comparisonByCount != 0
? comparisonByCount
: new IntegerListValueComparer().Compare(this, other);
}
}
// *************************************************************
// These are the concrete classes specified in the requirements.
// *************************************************************
public class RandomIntegerListThatComparesByValues :
IntegerListThatComparesByValues
{
public RandomIntegerListThatComparesByValues(int size)
: base(new RandomIntegerListPopulator(), size)
{ }
}
public class RandomIntegerListThatComparesByLength :
IntegerListThatComparesByLength
{
public RandomIntegerListThatComparesByLength(int size)
: base(new RandomIntegerListPopulator(), size)
{ }
}
// *************************************************************
// The rest is all testing.
// *************************************************************
// Allows me to create class instances that contain the numbers
// I specify instead of random numbers so that I can create
// test cases.
public class IntegerListPopulatorTestDouble : IIntegerListPopulator
{
private readonly int[] _values;
public IntegerListPopulatorTestDouble(params int[] values)
{
_values = values;
}
public void PopulateList(List<int> target, int size)
{
target.AddRange(_values.Take(size));
}
}
[TestClass]
public class IntegerListThatComparesByValuesTests
{
[TestMethod]
public void EmptyListsAreEqual()
{
var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(), 0 );
var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(), 0);
Assert.AreEqual(0, list1.CompareTo(list2));
}
[TestMethod]
public void ListsWithSameValuesAreEqual()
{
var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1,2,3), 3);
var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1,2,3), 3);
Assert.AreEqual(0, list1.CompareTo(list2));
}
[TestMethod]
public void ListsOfSameLengthComparedByFirstNonEqualValue()
{
var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 4), 3);
var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
Assert.IsTrue(list1.CompareTo(list2) > 0);
}
[TestMethod]
public void MissingElementsOfListAreSortedAsZeros()
{
var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3, 4), 4);
var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
var comparison = list1.CompareTo(list2);
Assert.IsTrue(comparison > 0);
comparison = list2.CompareTo(list1);
Assert.IsTrue(comparison < 0);
}
[TestMethod]
public void MissingElementsOfListAreSortedAsZeros_Case2()
{
var list1 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3, -4), 4);
var list2 = new IntegerListThatComparesByValues(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
Assert.IsTrue(list1.CompareTo(list2) < 0);
Assert.IsTrue(list2.CompareTo(list1) > 0);
}
}
[TestClass]
public class IntegerListThatComparesByLengthTests
{
[TestMethod]
public void ListsAreComparedByLength()
{
var list1 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3, 4), 4);
var list2 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
Assert.IsTrue(list1.CompareTo(list2) > 0);
Assert.IsTrue(list2.CompareTo(list1) < 0);
}
[TestMethod]
public void ListsOfEqualLengthAreComparedByValue()
{
var list1 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 4), 3);
var list2 = new IntegerListThatComparesByLength(new IntegerListPopulatorTestDouble(1, 2, 3), 3);
Assert.IsTrue(list1.CompareTo(list2) > 0);
Assert.IsTrue(list2.CompareTo(list1) < 0);
}
}
I don't know if they teach unit testing in school, but this illustrates why they should. While the logic isn't incredibly difficult, it's likely that the first stab at it isn't going to be perfect. Without creating some test cases to run against this, how could you know if you got it right? How would your instructor know? What if you fix one bug but in doing so break the other test case? And then if you have to debug, every single time you debug you get different random numbers. Scary.
(It was the randomness that really threw me off. If the numbers are always random, how does the instructor expect to observe the results and know that they're correct? It's possible but eyeballing it isn't the most reliable way to know that it works.)
In real life, I wouldn't want to put even slightly complex logic into a production system without having these unit tests to tell me that it works as expected.
Also, when you read a description of requirements it can be confusing and unclear. But assuming that I understood the requirements, now you can look at the unit tests and get a clearer idea of what this code is supposed to do. Otherwise someone else could need to make changes, and if they can't tell what it's supposed to do, they can't tell if they're breaking it. If it turns out that I got the requirements wrong then I can change the tests to fit the right requirements. The tests will fail and then I modify the code until the tests pass.

Interesting "params of ref" feature, any workarounds?

I wonder if there's any way something like this would be possible for value types...
public static class ExtensionMethods {
public static void SetTo(this Boolean source, params Boolean[] bools) {
for (int i = 0; i < bools.Length; i++) {
bools[i] = source;
}
}
}
then this would be possible:
Boolean a = true, b, c = true, d = true, e;
b.SetTo(a, c, d, e);
Of course, this does not work because the bools are a value type so they are passed into the function as a value, not as a reference.
Other than wrapping the value types into reference types (by creating another class), is there any way to pass a variable into function by the reference (ref) while using params modifier?
This is not possible. To explain why, first read my essay on why it is that we optimize deallocation of local variables of value type by putting them on the stack:
https://web.archive.org/web/20100224071314/http://blogs.msdn.com/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx
Now that you understand that, it should be clear why you cannot store a "ref bool" in an array. If you could, then you could have an array which survives longer than the stack variable being referenced. We have two choices: either allow this, and produce programs which crash and die horribly if you get it wrong -- this is the choice made by the designers of C. Or, disallow it, and have a system which is less flexible but more safe. We chose the latter.
But let's think about this a little deeper. If what you want is to pass around "thing which allows me to set a variable", we have that. That's just a delegate:
static void DoStuff<T>(this T thing, params Action<T>[] actions)
{
foreach(var action in actions) action(thing);
}
...
bool b = whatever;
b.DoStuff(x=>{q = x;}, x=>{r = x;} );
Make sense?
There isn't really a way. You could do something like this:
public static void Main(string[] args)
{
BooleanWrapper a = true, b = true, c = true, d = true, e = new BooleanWrapper();
b.SetTo(a, c, d, e);
}
public static void SetTo(this BooleanWrapper sourceWrapper, params BooleanWrapper[] wrappers)
{
foreach (var w in wrappers)
w.Value = sourceWrapper.Value;
}
public class BooleanWrapper
{
public BooleanWrapper() { }
public BooleanWrapper(Boolean value)
{
Value = value;
}
public Boolean Value { get; set; }
public static implicit operator BooleanWrapper(Boolean value)
{
return new BooleanWrapper(value);
}
}
But then again how is that any better than just doing this:
public static void Main(string[] args)
{
Boolean[] bools = new Boolean[5];
bools.SetTo(bools[1]); // Note I changed the order of arguments. I think this makes more sense.
}
public static void SetTo(this Boolean[] bools, Boolean value)
{
for(int i = 0; i < bools.Length; i++)
bools[i] = value;
}
After all, an array is a sequence of variables. If you need something that behaves like a sequence of variables, use an array.
Unfortunately the community of Java, and now .NET, developers decided that less flexibility in the name of "safety" is the preferred solution, and to achieve the same result with less lines of code you have to opt for extraordinary complexity (all those class structures, delegates, etc.).
In Delphi I could simply do something like this:
var
a: integer; f: double; n: integer;
sscanf(fmtstr, valuestr, [#a, #f, #n]);
//<-- "sscanf" is a function I wrote myself that takes an open array of pointers.
In C# You would have to do:
int a; double f; int n;
object [] o = new object[];
sscanf(fmtstr, valuestr, ref o);
a = o[0];
f = o[1];
n = o[2];
That's 5 lines of code to do what I could do in 1 line of Delphi code. I think there is a formula somewhere that the likelihood of bugs in code increases geometrically with the number of lines of code; so if you have 20 lines of code you're code is 4 times more likely to have bugs than if you have 10.
Of course, you can decrease your # lines of code by using the delegate with all those weird angle brackets and strange syntax, but I would think that's also a haven for bugs.
Here is some interesting solution:
public delegate RecursionRefFunc<T> RecursionRefFunc<T>(ref T arg);
public static RecursionRefFunc<T> Boo<T>(ref T input)
{
Console.WriteLine(input); // Work in here
return Boo;
}
public static void Main(string[] args)
{
int x1 = 1, x2 = 2, x3 = 3, x4 = 4, x5 = 5;
Boo(ref x1)(ref x2)(ref x3)(ref x4)(ref x5);
}
// Output: //
// 1
// 2
// 3
// 4
// 5
Delegate can declare in recursion.
Return a function outside and call again.
And you will be killed by the code reviewer.
Advertisement OW<: CWKSC/MyLib_Csharp
This would not be possible even if bools were reference types. While a class is a reference type, the variable in the Boolean[] is still a value, it's just that the value is a reference. Assigning the value of the reference just changes the value of that particular variable. The concept of an array of ref variables doesn't make sense (as arrays are, by their nature, a series of values).

Categories

Resources