IList<IWhatever> as a method parameter - c#

I have two IList<ICat> and I'm trying to create a method which takes an IList<ICat> and does some work. I'm having problems trying to pass either an IList<PussyCat> or IList<OtherCat> to it, both PussyCat and OtherCat implement ICat.
I've tried:
List<PussyCat> cats = ...
DoWork((IList<ICat>)cats);
and just
DoWork(cats);
But neither compile. Any ideas?

C# generics are invariant. It means List<string> is not a List<object>.
C# 4.0 introduces safe covariance/contravariance but still, you wouldn't be able to pass List<string> as List<object>. The reason is:
List<string> x = new List<string>();
List<object> o = x; // assume this statement is valid
o.Add(5); // Adding an integer to a list of strings. Unsafe. Will throw.
Arrays, on the other hand are covariant. You can pass a string[] to a method that expects object[].

There are two alternatives:
Make your method like this:
public void DoWork< T > (IList< T > cats_) where T : ICat
{
//Do work;
}
The other possibility is to have a method like
public void DoWork(IList< ICat > cats_)
{
//Do work;
}
and call it in the following manner:
{
//....Assuming: IList<PussyCat> iListOfPussyCats
List<PussyCat> pussyCats = new List<PussyCats>(iListOfPussyCats);
DoWork(pussyCats.ConvertAll<ICat>( c => c as ICat);
}

If the method doesn't truly require direct indexing (IList<T>) and doesn't require adding/removing items (ICollection<T>), then pass an IEnumerable<T>. The Cast<T>() extension methods allow casting any IList of [insert ICat-derived type] to be passed as an IEnumerable<ICat>.

Till C# 4.0 arrives which has support for co and contra variance you might be able to get away with something like this:
public void DoWork(IEnumerable<ICat> cats)
{
//do something
}
List<PussyCat> pussyCats = new List<PussyCat>;
List<OtherCat> otherCats = new List<OtherCat>;
DoWork(pussyCats.OfType<ICat>);
DoWork(otherCats.OfType<ICat>);

Related

How Can I Retrieve the Underlying List of an IEnumerable Without Creating a New List?

When using IEnumerable I'm trying to avoid multiple enumerations. I know I can just use LINQ's .ToList() and be done with it, but that can be a lot of unnecessary list creation. I'd like to:
check and see if the underlying type is a List, and if so return that instance, otherwise
.ToList() it and return the new List
My thought was to use something akin to:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo(IEnumerable<T> enumerable)
{
var list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}
... but it appears from the documentation that the as operator just performs a cast, which would create a new List rather than returning the underlying one, would it not?
If so, how can I retrieve the underlying list instead of creating a new one?
The as operator does not create a new list. It only checks type and perform cast if type is compatible.
The code in the post is logically correct and matches how many LINQ methods are implemented (for example see source of Enumerable.Count which casts to ICollection to see if it can skip enumeration of items).
Note that it is important to cast to correct generic version of list or maybe one of its interfaces - IList would work if you must use non-generic version. Beware of the fact that List<T> is not co/contra-variant and type must match exactly unlike in case of covariant IEnumerable<out T> where you can cast parameter to IEnumerable<TBase> if IEnumerable<TDerived> passed.
Maybe you wanted to do this:
public void Fee()
{
var list = new List<string>(); // I want to retrieve this instance in Foo
Foo(list);
}
public void Foo<T>(IEnumerable<T> enumerable)
{
List<T> list = enumerable as List<T> ?? enumerable.ToList();
// do stuff with original list
}

Weird behavior with lists with a derived generic type

So I have a list of a base type. This base type has two deriving classes, one of which is generic. I tried to foreach through the list and found this weird behavior.
public static void Test<T>()
{
List<Base> myList = new List<Base>();
myList.Add(new aDerived());
myList.Add(new tDerived<T>());
foreach (tDerived<T> lGen in myList as List<tDerived<T>>)
{
// This is fine
}
foreach (aDerived lDerived in myList as List<aDerived>)
{
// Error: Can't convert... via a reference conversion etc
}
}
//Simple classes to repro:
class Base
{
}
class aDerived : Base
{
}
class tDerived<T> : Base
{
}
This reports error CS0039 If the type is specified, ie instead of having a parametric method I replace T with, say, int, I get the same error on the first loop too.
Question: why does this happen. I can't see anything inherently impossible, I could, after all, do the as on the returned type of each iteration. On the flip side, why does it work if the thing is a template class, it seems like that shouldn't help it work.
None of the below operations can be done. Not sure but as far as compiler is aware of aDerived type so it informs you that it cannot be done (compile time error). But in the second case the compiler has not enough information about the exact type of tDerived<T> (as it will be specified in runtime -because your method is a generic method), so it will not inform you. But when running the code it will throw exception.
var result1 = myList as List<aDerived>; //Compile-time error
var result2 = myList as List<tDerived<T>> //null
More intersting: This can be done with no compile time error:
List<Base> myList = new List<Base>();
var result = myList as List<T>; //No Compile-time error
Also this link may be useful: Covariance and Contravariance.
This issue is you are trying to convert a list of aDerived and tDerived into one or the other. This wont work as its like trying to convert a list of fruit (bananas and apples) into a list of apples. What you can do however is use the OfType extension to iterate over the items of the correct type:
foreach (tDerived<T> lGen in myList.OfType<tDerived<T>>())
{
Console.WriteLine("tDerived found!");
}
foreach (aDerived lDerived in myList.OfType<aDerived>())
{
Console.WriteLine("aDerived found!");
}
MSDN OfType: http://msdn.microsoft.com/en-us/library/bb360913(v=vs.100).aspx

pass by reference and not returning a value

Let's say I have a method that calls another method with some parameters, something like this:
public void SomeMethod()
{
List<SomeObject> TheList = SomeQueryThatReturnsTheList();
TheList = DoSomeWorkWithList(TheList);
}
public List<SomeObject> WorkForList(List<SomeObject> TheListAsParameter)
{
foreach (SomeObject x in TheListAsParameter)
{
....
}
return TheListAsParameter;
}
As you can see, the method WorkForList returns the list it received. My question is this: if I don't return the list and rewrite the signature as public void WorkForList(List<SomeObject> TheListAsParameter) is pass by reference in c# going to mean that TheList in SomeMethod is going to be updated with the work that's done in the WorkForList method? If so, will the following code work the same:
public void SomeMethod()
{
List<SomeObject> TheList = SomeQueryThatReturnsTheList();
DoSomeWorkWithList(TheList);
}
public void WorkForList(List<SomeObject> TheListAsParameter)
{
....
}
Thanks.
Well if you don't use the ref keyword, its address will be passed by value, meaning you will be able to change its element, but you can't initialized it or can't assign it null. for example. If in your method you do:
public void WorkForList(List<SomeObject> TheListAsParameter)
{
TheListAsParameter = null;
}
You will not see the difference in the caller.
You should see this article: Parameter passing in C# by Jon Skeet
In this case, the code will do what you want to do, BUT bear in mind two things:
C# is "pass by value" language. It passes the address of the object, so this will only work if you work with this instance, not change the instance itself. For that, you should use ref keyword, but that usually ends up with harder to read code.
returning objects vastly improves readability - what you do is considered a side-effect, a user of your method (another team member maybe) may not be aware you are modifying the list contents.
Yes, you can do that. But returning the object might be useful, for clarity, and to allow methods chaining.
For example with your first WorkForList method :
yourObject.WorkForList(list).DoSomethingWithTheReturnedList();
Yes - this should behave exactly as you've described... Surely you can just test the code you've already written?
Yes, as long as your WorkForList() method doesn't have a line like this:
TheListAsParameter = something;
then any changes you make to TheListAsParameter will be reflected in the calling method without returning it.
To speak precisely here, in this case you aren't passing a variable by reference. You are passing a reference type by value. Pass by reference involves the use of the ref keyword in C#.
As long as you don't use foreach to modify values of the list:
foreach (SomeObject x in TheListAsParameter)
{
....
}
As you are not allowed to modify the contents of a Collection you are walking through using foreach.

Generic Method returning different generic collections

Hi I have a Method like this:
public T LoadR<T, K>()
where T : ObservableCollection<K>, new()
where K : IStoreElement, new() {
T onC = new T();
//....
return (onC);
}
Further I have a class XObservableCollection which derivies from ObservableCollection.
It works as it is when I call:
Categories = LoadR<ObservableCollection<Category>,Category>();
Products = LoadR<XObservableCollection<Product>,Product>();
What I want to do is to make a call like this (avoid passing the K as extra parameter):
Categories = LoadR<ObservableCollection<Category>>();
Products = LoadR<XObservableCollection<Product>>();
I know that I could write an extension for it.
But I curious if there is a way to achive this without it.
Manfred
I don't think you can.
C# has a mechanism to understand generic agruments from method paramaters when used in it, but not from other generic arguments of the same method. It doesn't work.
Maybe you can change your signature to:
private static ObservableCollection<K> LoadR<K>(.ObservableCollection<K> onC)
where K : IStoreElement, new()
{
//....
return onC;
}
Usage would be:
static void TestLoad()
{
var result1 = LoadR(new ObservableCollection<Something>());
var result2 = LoadR(new DerivedClassFromObservableCollection<Something>());
}
Which I agree is not that good, and I can se it's not what you are looking for.
But just because C# wouldn't let you try to infer the types from the generic arguments.
.....
The other way aroudn is to make it use a specific collection. I can see you don't want this even more.
So, one thing you can do is make it return IEnumerable instead, then you usage will be something like:
static void TestLoad()
{
var result1 = new ObservableCollection( LoadR<Something>() );
var result1 = new DerivedClassFromObservableCollection( LoadR<Something>() );
}
Which may not be good also if you want to do fancy things with the collection itself, because you no-longer own it.
....
I would go for the very first option myself.

Cast from filtered custom List<T> with LINQ

I have a custom list which inherits from Generic.List<T> like this:
public class TransferFileList<T> : List<TransferFile> { .. }
When I set (where 'Files' is a TransferFileList<T>):
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
the 'files' object resolves as System.Collections.Generic.List<TransferFile>, not TransferFileList<T>, which is what I would expect as it was what was being filtered through the Where, so how could I successfully return a list of TransferFileList<T> into 'files'?
I did try:
var files = uploadResponse.Files.Where(x => !x.Success).ToList()
as TransferFileList<TransferFile>;
but using that safe cast, it just resolves as null.
Thanks guys and gals.
First, I have to ask why you are inheriting from List<T>? 99% of the time that's a bad idea.
If you want to extend the functionality of a list, use extension methods:
public static something PrintErrors(this List<TransferFile> list)
{
//do your printing logic
}
On to the answer: ToList() operates on an IEnumerable<T> and converts the members of the sequence to a List of the same type. Since you inherit from List<T> which implements IEnumerable<T>, that's what happens there.
Where() works the same way - operates on an IEnumerable<T> and returns an IEnumerable<T>.
To get some arbitrary list-like object back, like you have, you need to add the items in a sequence to your custom list, like so:
var myFiles = new TransferFileList<TransferFile>();
myFiles.AddRange(originalFileList.Where(condition));
You can add an extension method for IEnumerable<TransferFile> to handle that scenario:
public static TransferFileList ToTransferFileList(
this IEnumerable<TransferFile> files)
{
return new TransferFileList(files);
}
// ...
var files = uploadResponse.Files.Where(x => !x.Success).ToTransferFileList();
This provides you with the TransferFileList instead of just a List<TransferFile>. Note the reason your as returns null is because while TransferFileList is a List<TransferFile>, the same does not hold in the other direction. That is, your List<TransferFile> is NOT a TransferFileList object.
I agree with #RexM that any attempt at subclassing List<T> be avoided due to the multitude of pitfalls associated. I suggest Composition (Has-A rather than Is-A) or sticking with the base class library collections instead.
Thanks guys.
I like SLV's extension approach, but is there any other straight casting approach?
If not I might just go with the reverted in-line approach I was hoping to avoid:
var transferFiles = new TransferFileList<TransferFile>();
if (files != null)
transferFiles.AddRange(files);

Categories