Method should throw exception but it doesn't - c#

I wrote a small extensionmethod which finds the indexes of the given string in any IEnumerable.
public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
if (itemList == null)
throw new ArgumentNullException("itemList");
if (indexesToFind == null)
throw new ArgumentNullException("indexToFind");
List<string> enumerable = itemList as List<string> ?? itemList.ToList();
for (int i = 0; i < enumerable.Count(); i++)
{
if (enumerable[i] == indexesToFind)
yield return i;
}
}
As you can see above, an ArgumentNullException is thrown if itemList is null. Plain and simple.
When running my unittest on the above method, I expect and exception of type ArgumentNullException, because itemList is null. However, the test comes out false because no exception gets thrown.
How is that possible? The logic seems quite clear. See the test below.
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void FindIndexesOfTest2()
{
string[] items = null;
IEnumerable<int> indexes = items.FindIndexesOf("one");
}
Where am I going wrong in my logic; why is it not throwing an ArgumentNullException?

The problem is that enumerators using yield is lazily evaluated.
Since you're not iterating over the collection returned, the method hasn't actually executed.
The correct way to do this is to split the method in two:
public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
if (itemList == null)
throw new ArgumentNullException("itemList");
if (indexesToFind == null)
throw new ArgumentNullException("indexToFind");
return FindIndexesOfImpl(itemList, indexesToFind);
}
private static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
List<string> enumerable = itemList as List<string> ?? itemList.ToList();
for (int i = 0; i < enumerable.Count(); i++)
{
if (enumerable[i] == indexesToFind)
yield return i;
}
}
Here the first method will execute when you call it, and return a lazily evaluated enumerator that hasn't, until you iterate over it.
Though, I would suggest you also change the latter method here to be truly lazily evaluated. The fact that the method caches the entire itemList just to be able to use indexes is unnecessary, and you can in fact rewrite it without it:
public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
var index = 0;
foreach (var item in itemList)
{
if (item == indexesToFind)
yield return index;
index++;
}
}
You can also use the LINQ extension methods to do it though this involves constructing a temporary object for each element, unsure whether it is worth it, I'd go with the one just above here instead:
public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
return itemList
.Select((item, index) => new { item, index })
.Where(element => element.item == indexesToFind)
.Select(element => element.index);
}
With this last method you can move this back up into the main method because you're no longer using yield:
public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
if (itemList == null)
throw new ArgumentNullException("itemList");
if (indexesToFind == null)
throw new ArgumentNullException("indexToFind");
return itemList
.Select((item, index) => new { item, index })
.Where(element => element.item == indexesToFind)
.Select(element => element.index);
}

Related

Cannot convert from 'string' to 'T'

I have created a helper function which accepts a string array as input and the index of the column as selected output.
public static List<T> GetByIndex<T>(string[] data, int index)
{
List<T> list = new List<T>();
foreach (var item in data)
{
if (item.Contains("|"))
{
string[] s = item.Split("|");
list.Add(s[index]);
}
}
return list;
}
At line list.Add(s[index]); i get the error Cannot convert from 'string' to 'T'.
How can i make this method generic?
The function should return string or integer or even maybe boolean
How can i make this method generic?
You shouldn't. You're only dealing with strings so make the method return a List<string>
public static List<string> GetByIndex(string[] data, int index)
{
var list = new List<string>();
foreach (var item in data)
{
if (item.Contains("|"))
{
string[] s = item.Split("|");
list.Add(s[index]);
}
}
return list;
}
If you need to also support int then just make an overload which returns a List<int> and use int.Parse / int.TryParse.
It seems that you don't want generic T at all: what is the expected output for, say, T == IComparer<DateTime>?
I suggest getting rid of T and returning List<string>:
public static List<string> GetByIndex(string[] data, int index) {
// Public method's arguments validation
if (data is null)
throw new ArgumentNullException(nameof(data));
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index));
List<string> list = new List<string>();
foreach (string item in data) {
// Do not forget about nulls
if (item != null) {
// We can stop earlier on Split:
// if, say, index == 2 we don't want all 12345 items
string[] s = item.Split('|', index + 2);
// s can well be too short
if (s.Length > index)
list.Add(s[index]);
}
}
return list;
}
If you want to return a List<T>, you need to provide some way of converting each string into a T.
Such as:
public static List<T> GetByIndex<T>(
string[] data,
int index,
Func<string, T> convertItem)
{
List<T> list = new List<T>();
foreach (var item in data)
{
if (item.Contains("|"))
{
string[] s = item.Split("|");
list.Add(convertItem(s[index]));
}
}
return list;
}
And you could provide convenience overloads for the types you commonly use.
For example:
public static List<int> GetIntByIndex(string[] data, int index)
{
return GetByIndex(data, index, int.Parse);
}

Check List<T> for duplications with optional words to exclude

I have a method as below. Method return either false/true either when list contains duplicates or not. I would like to extend my method to say for instance (optional) that i want to exclude specific items from check. For instance i want to check entire list as it is now or i want to say for instance exclude: string.empty items or for instance string.empty and "some word". Is it possible?
public static bool IsListContainsDuplicates<T>(List<T> list)
{
return list.GroupBy(n => n).Any(c => c.Count() > 1);
}
public static bool ContainsDuplicates<T>(this IEnumerable<T> items, IEnumerable<T> itemsToExclude = null)
{
if (itemsToExclude == null) itemsToExclude = Enumerable.Empty<T>();
return items.Except(itemsToExclude)
.GroupBy(n => n)
.Any(c => c.Count() > 1);
}
But i'd prefer this implementation because it's more performant:
public static bool ContainsDuplicates<T>(this IEnumerable<T> items, IEnumerable<T> itemsToExclude = null)
{
if (itemsToExclude == null) itemsToExclude = Enumerable.Empty<T>();
HashSet<T> set = new HashSet<T>();
return !items.Except(itemsToExclude).All(set.Add);
}
Instead of making your method more complicated, you should open it more to combine it with others:
public static class MyLinqMethods
{
public static bool HasDuplicates<T>(this IEnumerable<T> sequence)
{
return sequence.GroupBy(n => n).Any(c => c.Count() > 1);
}
}
Now you can use it with Linq:
var original = new[] { string.Empty, "Hello", "World", string.Empty };
var duplicatesInOriginal = original.HasDuplicates();
var duplicatesIfStringEmptyIsIgnored = original.Where(o => o != string.Empty).HasDuplicates();
You can use Except(). From MSDN:
Produces the set difference of two sequences by using the default
equality comparer to compare values.
return list.Except(listToExclude).GroupBy(n => n).Any(c => c.Count() > 1);
This will also help, using a 'params' in arguments and then doing Except()
public static bool IsListContainsDuplicates<T>(List<T> list, params T[] optional)
{
return list.Except(optional).GroupBy(n => n).Any(c => c.Count() > 1);
}
You can call like this if you doesn't want to exclude anything:
IsListContainsDuplicates(list)
Else, just pass the params values, for example, if the list is an integer list then,
IsListContainsDuplicates(list,5,4)

Unexpected behavior using Enumerable.Empty<string>()

I would expect Enumerable.Empty<string>() to return an empty array of strings. Instead, it appears to return an array with a single null value. This breaks other LINQ operators like DefaultIfEmpty, since the enumerable is not, in fact, empty. This doesn't seem to be documented anywhere, so I'm wondering if I'm missing something (99% probability).
GameObject Class
public GameObject(string id,IEnumerable<string> keywords) {
if (String.IsNullOrWhiteSpace(id)) {
throw new ArgumentException("invalid", "id");
}
if (keywords==null) {
throw new ArgumentException("invalid", "keywords");
}
if (keywords.DefaultIfEmpty() == null) { //This line doesn't work correctly.
throw new ArgumentException("invalid", "keywords");
}
if (keywords.Any(kw => String.IsNullOrWhiteSpace(kw))) {
throw new ArgumentException("invalid", "keywords");
}
_id = id;
_keywords = new HashSet<string>(keywords);
}
Test
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void EmptyKeywords() {
GameObject test = new GameObject("test",System.Linq.Enumerable.Empty<string>());
}
It looks like you expect this condition:
keywords.DefaultIfEmpty() == null
to evaluate to true. However DefaultIfEmpty returns a singleton sequence containing the default for the element type (string in this case) if the source sequence is empty. Therefore it will return a sequence containing null. This is not itself null however so the condition returns false.
You are misinterpreting the implementation of DefaultIfEmpty, here is it's implementation from the reference source.
public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source) {
return DefaultIfEmpty(source, default(TSource));
}
public static IEnumerable<TSource> DefaultIfEmpty<TSource>(this IEnumerable<TSource> source, TSource defaultValue) {
if (source == null) throw Error.ArgumentNull("source");
return DefaultIfEmptyIterator<TSource>(source, defaultValue);
}
static IEnumerable<TSource> DefaultIfEmptyIterator<TSource>(IEnumerable<TSource> source, TSource defaultValue) {
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) {
do {
yield return e.Current;
} while (e.MoveNext());
}
else {
yield return defaultValue;
}
}
}
So what it does is if a IEnumerable<T> is not empty it simply returns the IEnumerable<T>, if the IEnumerable<T> is empty it returns new a IEnumerable<T> with one object in it with the value default(T). It will never return null which is what your test is testing for. If you wanted to test this you would need to do
if(keywords.DefaultIfEmpty().First() == null)
However this is going to cause the IEnumerable<string> to be evaluated multiple times. I would drop the LINQ and just do like the LINQ method does and do it the long way (this also gets rid of the extra evaluation you had inside new HashSet<string>(keywords)).
public GameObject(string id,IEnumerable<string> keywords)
{
if (String.IsNullOrWhiteSpace(id)) {
throw new ArgumentException("invalid", "id");
}
if (keywords==null) {
throw new ArgumentException("invalid", "keywords");
}
_keywords = new HashSet<string>();
using (var enumerator = keywords.GetEnumerator())
{
if (e.MoveNext())
{
do
{
if(e.Current == null)
throw new ArgumentException("invalid", "keywords");
_keywords.Add(e.Current);
} while (e.MoveNext());
}
else
{
throw new ArgumentException("invalid", "keywords");
}
}
_id = id;
}
This makes it so you only loop once over the IEnumerable<string>.
Does this solve your problem?
public GameObject(string id, IEnumerable<string> keywords) {
if (String.IsNullOrWhiteSpace(id)) {
throw new ArgumentException("invalid", "id");
}
if (keywords == null || !keywords.Any()
|| keywords.Any(k => String.IsNullOrWhiteSpace(k))) {
throw new ArgumentException("invalid", "keywords");
}
_id = id;
_keywords = new HashSet<string>(keywords);
}
*Improved the code with suggestions from #ScottChamberlain & #ginkner

C# Lists reference with two?

I need 2 lists which are separate and one list containing the items of these two lists.
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
List<int> list3 = list1 & list2
When I add some integers in list1, I would like them to appear in list3 as well.
I want the same behavior when a new item is added into list2.
A reference of more than one lists.
Is this possible?
No, you can't do that with List<T> directly. However, you could declare:
IEnumerable<int> union = list1.Union(list2);
Now that will be lazily evaluated - every time you iterate over union, it will return every integer which is in either list1 or list2 (or both). It will only return any integer once.
If you want the equivalent but with concatenation, you can use
IEnumerable<int> concatenation = list1.Concat(list2);
Again, that will be lazily evaluated.
As noted in comments, this doesn't expose all the operations that List<T> does, but if you only need to read from the "combined integers" (and do so iteratively rather than in some random access fashion) then it may be all you need.
Is it possible?
Not with List since it's a static data structure - however you could use a query that is the concatenation of the two lists. Then whenever you enumerate the query it will show the current contents:
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
IEnumerable<int> list3 = list1.Concat(list2);
But as soon as you materialize the query into a data structure (by calling ToList, ToArray, etc.) the contents are static and will not update if one of the underlying lists updates.
No. You can create your own custom type that exposes methods similar to what a List<T> does (an indexer, Add, Remove methods, etc.), possibly even implementing IList<T>, but it wouldn't be a List<T>.
public class CompositeList<T> : IList<T>
{
private IList<IList<T>> lists;
public CompositeList(IList<IList<T>> lists)
{
this.lists = lists;
}
public CompositeList(params IList<T>[] lists)
{
this.lists = lists;
}
public int IndexOf(T item)
{
int globalIndex = 0;
foreach (var list in lists)
{
var localIndex = list.IndexOf(item);
if (localIndex >= 0)
return globalIndex + localIndex;
else
globalIndex += list.Count;
}
return -1;
}
public void Insert(int index, T item)
{
//note that there is an ambiguity over where items should be inserted
//when they are on the border of a set of lists.
foreach (var list in lists)
{
//use greater than or equal instead of just greater than to have the inserted item
//go in the later of the two lists in ambiguous situations,
//rather than the former.
if (index > list.Count)
index -= list.Count;
else
{
list.Insert(index, item);
return;
}
}
//TODO deal with having no lists in `lists`
//TODO deal with having only empty lists in `lists`
throw new IndexOutOfRangeException();
}
public void RemoveAt(int index)
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
list.RemoveAt(index);
}
throw new IndexOutOfRangeException();
}
public T this[int index]
{
get
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
return list[index];
}
throw new IndexOutOfRangeException();
}
set
{
foreach (var list in lists)
{
if (index > lists.Count)
index -= list.Count;
else
list[index] = value;
}
throw new IndexOutOfRangeException();
}
}
public void Add(T item)
{
if (!lists.Any())
lists.Add(new List<T>());
lists[lists.Count - 1].Add(item);
}
public void Clear()
{
foreach (var list in lists)
list.Clear();
}
public bool Contains(T item)
{
return lists.Any(list => list.Contains(item));
}
public void CopyTo(T[] array, int arrayIndex)
{
if (array.Length - arrayIndex - Count < 0)
throw new ArgumentException("The array is not large enough.");
int iterations = Math.Min(array.Length, Count);
for (int i = arrayIndex; i < iterations; i++)
array[i] = this[i];
}
public int Count
{
get { return lists.Sum(list => list.Count); }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
foreach (var list in lists)
{
if (list.Remove(item))
return true;
}
return false;
}
public IEnumerator<T> GetEnumerator()
{
return lists.SelectMany(x => x).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

linq extension method to take elements from the end of the sequence

There is the enumerable extension method
Take<TSource>(
IEnumerable<TSource> source,
int count
)
which takes the first count elements from the start.
Is there a way to take the elements from the end?
or even better a way to take the elements from an offset to the end?
Thanks
finiteList.Reverse().Take(count).Reverse();
or
finiteList.Skip(finiteList.Count() - count)
There is some overhead in doing this so a custom method would be better.
Update: A custom method
public static class EnumerableExtensions
{
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) throw new ArgumentNullException("source");
if (count < 0) throw new ArgumentOutOfRangeException("count");
if (count == 0) yield break;
var queue = new Queue<T>(count);
foreach (var t in source)
{
if (queue.Count == count) queue.Dequeue();
queue.Enqueue(t);
}
foreach (var t in queue)
yield return t;
}
}
Update: Changed the code a littlebit with ideas from dtb´s answer :-)
Comment to Bear: Look at this example:
var lastFive = Enumerable.Range(1, 10).TakeLast(5);
var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way
Queue<int> q = (Queue<int>)lastFive2;
q.Dequeue();
//Is lastFive2 still last five? no...
You could potentially change the values of lastFive2 and therefore that approach can be unsafe or at least it´s not the functional way.
To Bear:
What I meant about safe is this:
var lastFive2 = Enumerable.Range(1, 10).TakeLast2(5); //Bear´s way
//some = Some method which you don't control - it could be from another assembly which represents a crazy plugin etc.
some(lastFive2);
//Now what?
In these cases you would have to make a copy to be sure. But in most cases your way would be fine - and a little bit more efficient than this so +1 :)
An idea is to use a queue which only have internal Enqueue etc.
MoreLINQ provides a TakeLast extension method:
var last10 = finiteList.TakeLast(10);
To take the elements from an offset to the end, Enumerable.Skip should do the trick:
var allFromOffsetToEnd = finiteList.Skip(offset);
#lasseespeholt:
public static class EnumerableExtensions
{
public static ReadOnlyEnumerable<T> AsReadOnly<T>(
this IEnumerable<T> source)
{
return new ReadOnlyEnumerable<T>(source);
}
}
public sealed class ReadOnlyEnumerable<T> : IEnumerable<T>
{
private readonly IEnumerable<T> _source;
public ReadOnlyEnumerable(IEnumerable<T> source)
{
if (_source == null)
{
throw new ArgumentNullException("source");
}
_source = source;
}
public IEnumerator<T> GetEnumerator()
{
return _source.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _source.GetEnumerator();
}
}
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int count)
{
if (source == null) throw new ArgumentNullException("source");
if (count < 0) throw new ArgumentOutOfRangeException("count");
if (count == 0)
return Enumerable.Empty<T>();
var queue = new Queue<T>(count);
foreach (var t in source)
{
if (queue.Count == count) queue.Dequeue();
queue.Enqueue(t);
}
return queue.AsReadOnly();
}
A note on performance. Plenty of answers here operating on IEnumerable<> and that is probably what you need and should use.
But if the datasets are large and of type List<> or similar, you can prevent a lot of unnecessary iterating with something like:
// demo, no errorhandling
public static IEnumerable<T> TakeFrom<T>(this IList<T> list, int offset)
{
for (int i = offset; i < list.Count; i += 1)
{
yield return list[i];
}
}

Categories

Resources