Cast class A to class B without generics - c#

I have two classes that have no connection to one another :
public class A
{
public String Address {get;set}
}
public class B
{
public String Address {get;set}
}
List<A> addressList = DB.Addresses.GetAll();
When I do
List<B> addressListOther = addressList.Cast<B>().ToList();
the output is :
Additional information: Unable to cast object of type 'A' to type 'B'.
Any idea how to fix that ?

You can use Select() instead of that way:
List<B> addressListOther = addressList.Select(a => new B { Address = a.Address}).ToList();
Or you can override explicit operator in class B:
public static explicit operator B(A a) // explicit A to B conversion operator
{
return new B { Address = a.Address };
}
And, then:
List<B> addressListOther = aList.Select(a => (B)a).ToList();
The reason of this exception:
Cast will throw InvalidCastException, because it tries to convert A to object, then cast it to B:
A myA = ...;
object myObject = myA ;
B myB= (B)myObject; // Exception will be thrown here
The reason of this exception is, a boxed value can only be unboxed to a variable of the exact same type.
Additional Information:
Here is the implemetation of the Cast<TResult>(this IEnumerable source) method, if you interested:
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source) {
IEnumerable<TResult> typedSource = source as IEnumerable<TResult>;
if (typedSource != null) return typedSource;
if (source == null) throw Error.ArgumentNull("source");
return CastIterator<TResult>(source);
}
As you see, it returns CastIterator:
static IEnumerable<TResult> CastIterator<TResult>(IEnumerable source) {
foreach (object obj in source) yield return (TResult)obj;
}
Look at the above code. It will iterate over source with foreach loop, and converts all items to object, then to (TResult).

They do not inherit from each other so you can't do that. The Cast<T> iterates over the collection and tries to cast the items to the specified type.
If you do the following you will fail to0:
A a = new A { Address = "a"};
B b = (B)a; // Compile error of: Cannot convert type A to B
Instead use Select to project new B items.
List<B> addressListOther = addressList.Select(a => new B { Address = a.Address}).ToList();
Another way is to override the implicit operator in class A:
public static implicit operator B(A a)
{
return new B { Address = a.Address };
}
And then the followingcode will word:
List<A> aList = new List<A> { new A { Address = "a" } };
List<B> bList = aList.Select(a => (B)a).ToList();

The simpliest way for me is to introduce a superclass.
public class ClassWithAddress
{
public string Address{get;set;}
}
Then you'll derive every class from that class and delete the address property like this:
public class A : ClassWithAddress
{
}
public class B : ClassWithAddress
{
}
After that you can do your list operations concerning the Address Property with the superclass ClassWithAddress.

Related

Casting an Enumerable.Empty<>() into another class that implements IEnumerable returns null

I have a class that implements IEnumerable:
public class A { }
public class B : IEnumerable<A> { }
How can i use class B as Enumerable.Empty<A>() in this case?
I mean that casting like this Enumerable.Empty<A>() as B returns null. Why it does? Should i implement any specific constructor or a method? Or is it an forbidden operation and I should to do it in the other way?
Enumerable.Empty<T>() is implemented as:
internal class EmptyEnumerable<T>
{
public static readonly T[] Instance = new T[0];
}
public static IEnumerable<T> Empty<T>()
{
return EmptyEnumerable<T>.Instance;
}
If you remove the optimization to cache the empty array, you could rewrite this as:
public static IEnumerable<T> Empty<T>()
{
return new T[0];
}
So Enumerable.Empty<T>() just returns an empty array of type T.
You wouldn't write:
B b = new A[0];
This doesn't make sense: a B isn't an array of instances of A.
For the same reason, you can't write:
B b = Enumerable.Empty<A>();

In C#, how can I convert my array from IEnumerable<IMyInterface> to IEnumerable<T>?

In C#, I want to take an array of Type "T" where I know "T" supports the interface "IMyInterface" and:
Cast it as array of "IMyinterface"
Call a method on that array that will filter the list
Cast it back to the original type T list.
1 and 2 above work fine, but I am running into issues on step #3.
Here is my code:
IEnumerable<IMyInterface> castedArray = originalTypedArray as IEnumerable<IMyInterface>;
if (castedArray != null)
{
var filteredArray = castedArray.Where(r => r.Ids.Contains(MyId)).ToList();
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
if (castedBackToOriginalTypeArray == null)
{
current = new List<T>();
}
else
{
current = castedBackArray;
}
// I need to cast back, because only my Type T has the .Id property
List<int> ids = current.Select(r => r.Id).ToList();
}
The issue is on this line:
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
That always seem to return null (instead of the filtered array cast back to IEnumerable<T>.
Any suggestions here for what I might be doing wrong and how to correct cast an array of an interface back into an array of type T?
This works for me:
public class A : IA {
}
public interface IA {
}
List<A> l = new List<A> { new A(), new A(), new A() };
IEnumerable<IA> ias = l.Cast<IA>();
IEnumerable<A> aTypes = ias.Cast<A>();
Either you don't need to cast it to IEnumerable<IMyInterface>, or the runtime has correctly prevented you from writing buggy code.
Let's take a smaller example:
void SomeMethod<T>(IEnumerable<T> originalTypedArray, int MyId)
where T : class, IMyInterface
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is important
{
if (originalTypedArray != null)
{
var filteredArray = originalTypedArray.Where(r => r.Ids.Contains(MyId));
// No need to cast to `IEnumerable<T>` here - we already have ensured covariance
// is valid in our generic type constraint
DoSomethingExpectingIEnumerableOfIMyInterface(filteredArray);
}
}
void DoSomethingExpectingIEnumerableOfIMyInterface(IEnumerable<IMyInterface> src)
{
foreach (var thing in src)
{
}
}
However, if you're not getting the collection as IEnumerable<T>, then the runtime is correctly failing the cast:
void SomeMethod<T>(IEnumerable<IMyInterface> originalTypedArray, int MyId)
We could give it a bunch of IEnumerable<Apple> assuming Apple : IMyInterface. Then you try to cast it to IEnumerable<T> where T = Banana and boom, code broken.

Generic conversion method throw InvalidCastException

I want to implement same simple generic conversion method but on runtime I am getting an error.
So the scenario is quite simple. I have same service that return me list of items of type External. I have my own WrapperExternal class that simply wrap this class and expose some additional functionality to it. I have some another set of classes that inherit from WrapExternal and add different functionalities.
I want to create generic method that accept list of External list items and return list of items of specified type.
My application code:
static void Main(string[] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems<SubWrapperExternal>(items).ToList();
}
public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
{
return externalItems
.Where( item => true)
.Select(item => (T)item);
}
When you try to run this code you will get exception in line (T)item:
An unhandled exception of type 'System.InvalidCastException' occurred in ConsoleApplication1.exe
Additional information:
Unable to cast object of type 'ConsoleApplication1.WrapperExternal' to type 'ConsoleApplication1.SubWrapperExternal'.
Do you know how can I make it to works?
Test application code:
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var items = GetItemsFromServer();
var converted = ConvertItems<SubWrapperExternal>(items).ToList();
}
private static List<External> GetItemsFromServer()
{
return new List<External>
{
new External{Name = "A"},
new External{Name = "B"},
new External{Name = "C"},
};
}
public static IEnumerable<T> ConvertItems<T>(IEnumerable<External> externalItems) where T : WrapperExternal
{
return externalItems
.Where( item => true)
.Select(item => (T)item);
}
}
class External
{
public string Name { get; set; }
}
class WrapperExternal
{
public External External { get; private set; }
public WrapperExternal(External external)
{
External = external;
}
public static explicit operator WrapperExternal(External item)
{
return item != null ? new WrapperExternal(item) : null;
}
public static implicit operator External(WrapperExternal item)
{
return item != null ? item.External : null;
}
}
class SubWrapperExternal : WrapperExternal
{
public SubWrapperExternal(External external)
: base(external)
{
}
public static explicit operator SubWrapperExternal(External item)
{
return item != null ? new SubWrapperExternal(item) : null;
}
public static implicit operator External(SubWrapperExternal item)
{
return item != null ? item.External : null;
}
}
}
Conversion operators are a faff to use with generics - generics don't support any static operator overloads. Because of this, the (T) cast is performing a non-converting type check (generics need to use the same IL for every possible T, remember) - a basic castclass.
The only "simple" way of doing what you want is to cheat with dynamic:
return externalItems.Select(item => (T)(dynamic)item);
Since the C#-specific dynamic provider knows all the common rules of C#, it knows about conversion operators, and will apply them on-demand. There is a slight performance cost associated with this, but it isn't as bad as it looks at first glance, as the strategy is cached (as IL) once per type - it doesn't perform per-item reflection.

One liner for concatenation of two different lists of objects with the same interface

Consider this simple code:
public interface Iinterface { }
public class Foo : Iinterface { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
// Ok
IEnumerable<Iinterface> concats1 = bars.Cast<Iinterface>().Concat(foos.Cast<Iinterface>());
// Compilation error
IEnumerable<Iinterface> concats2 = bars.Concat(foos);
}
I want to concat two lists in one in one line, and keeping a type safety at compile time.
If for example, I delete the interface for the class Foo, this will still compile, but fail at runtime:
public interface Iinterface { }
public class Foo { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
IEnumerable<Iinterface> concats1 = bars.Cast<Iinterface>().Concat(foos.Cast<Iinterface>());
}
If I use OfType<T>(), this will not fail at runtime, but I want it to fail at compile time.
The best I could find is to use this 3 line of codes:
var list = new List<Iinterface>();
list.AddRange(bars);
list.AddRange(foos);
But for something that simple, I want to find a one-liner, and if possible retrieve an IEnumerable<Iinterface> instead of a List<Iinterface>.
Is there any way to achieve this?
You could just write your own method?
Since IEnumerable is covariant you only need to specify the base
var list= Combine<IInterface>(foos, bars);
private IEnumerable<T> Combine<T>(IEnumerable<T> ListA, IEnumerable<T> ListB)
{
foreach (T t in ListA)
yield return t;
foreach (T t in ListB)
yield return t;
}
Although you might as well just write
var list= foos.Concat<IInterface>(bars);
How about making your custom Concat extension method version:
public static class MyEnumerable
{
public static IEnumerable<T> Concat<T, T1, T2>(this IEnumerable<T1> source, this IEnumerable<T2> other)
where T1 : T
where T2 : T
{
return source.Cast<T>().Concat(other.Cast<T>());
}
}
Usage:
var items = foos.Concat<Iinterface, Foo, Bar>(bars);
It has compile-time safety, and will not compile if any of Foo and Bar does not implement Iinterface.
It should also support differed execution out of the box, because used LINQ methods do.
In fact, the concat method already did what I want, I juste need to help a little by specifying the return type, because there is no type inference on return types.
public interface Iinterface { }
public class Foo : Iinterface { }
public class Bar : Iinterface { }
[TestMethod()]
public void Test_Concat()
{
var bars = new List<Bar>();
var foos = new List<Foo>();
var list = foos.Concat<Iinterface>(bars);
}
I'm so used to use inference that I forgot that sometimes, compiler need hints!
You can do this instead:
public void Test_Concat2()
{
IEnumerable<Iinterface> bars = new List<Bar>();
IEnumerable<Iinterface> foos = new List<Foo>();
IEnumerable<Iinterface> concats = bars.Concat(foos);
}
If you then change the definition Foo or Bar so it no longer has the interface, you will get a compile-time error.
The problem appears to be in the Cast not the Concat. The declarations are as follows:
public static IEnumerable<TSource> Concat<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second);
public static IEnumerable<TResult> Cast<TResult>(this IEnumerable source);
In Concat, both enunerables are strongly typed, but Cast does not constrain the source type so the problem occurs at runtime instead of compile time.
Trying a MyCast implementation as follows fails at run time at (TResult) s
public static IEnumerable<TResult> MyCast<TResult>(this IEnumerable source)
{
var result = (from object s in source select (TResult) s).ToList();
return result.AsEnumerable();
}
If implementing a bit of type checking in a new Cast like so:
public static IEnumerable<TResult> MyCast2<TSource, TResult>(this IEnumerable<TSource> source)
where TSource : TResult
{
var result = (from object s in source select (TResult)s).ToList();
return result.AsEnumerable();
}
then the calling line needs two type params like so
IEnumerable<Iinterface> concats3 = bars.Concat(foos.MyCast2<Foo, Iinterface>());
and the compiler detects that Foo is not convertible to Iinterface.
Of course, specifying the return type on Concat is more concise.

Generic method with variance in C#?

Consider following classes (inheritance tree):
public class A {}
public class B: A {}
And this method:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
The compiler is unhappy. Error is Cannot convert expression type IList<B> to return type IList<A>. How do I solve this ? In another words how to specify that MyMethod will return IList<T> of T where T can be anything that inherits from A or instances of A itself ?
What you're asking for is impossible because IList<T> does not support variance -- you cannot use IList<B> anywhere that is expecting IList<A>. You'll have to explain more details of what you want in order to come up with a solution.
Possible solutions are:
public IList<A> MyMethod(){
IList<A> result = new List<A>();
//add some items to result
return result;
}
Or
public IEnumerable<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You cannot convert an IList<B> to IList<A>, even if B inherits from A. Otherwise, the user might attempt to add an instance of A that is not B into the list.
public void Example(){
IList<B> listB = new List<B>();
IList<A> listA = listB;
listA.Add(new A()); // Can't insert A into a list of B
}
Can you return IEnumerable<A> instead of IList<A>? IEnumerable<A> is covariant, unlike IList<A>.
how to specify that MyMethod will return IList of T where T can be anything that inherits from A or instances of A itself ?
You don't have to
You can just declare that it returns IList<A>. Why? Becase - given that B inherits from A - every item of B can be passed where the requiered type is A.
Call it polymorphism by inheritance, Liskov substitution principle, or method variance, the name doesn't matter. What matters is that the following works (tested on LinqPad):
public class A {}
public class B: A {}
public IList<A> MyMethod()
{
var result = new List<A>();
//add some items to result
result.Add(new B());
return result;
}
Genetic alternatives
In fact, you can tell that you are going to return a IList<TA> and request a few derived types (TB, TC...) to populate it with. That's right, the following example also works (tested on LinqPad):
void Main()
{
MyMethod<A, B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<TA> MyMethod<TA, TB, TC>()
where TB : TA, new()
where TC : TA, new()
where TA : class
{
var result = new List<TA>();
//add some items to result
result.Add(new B() as TA);
result.Add(new C() as TA);
return result;
}
Or if you want to keep a particular base type (say you want to return an IList<A> but it actually contains items of classes that derive from A, then you can do this:
void Main()
{
MyMethod<B, C>();
}
public class A {}
public class B: A {}
public class C: A {}
public IList<A> MyMethod<T1, T2>()
where T1 : A, new()
where T2 : A, new()
{
var result = new List<A>();
//add some items to result
result.Add(new T1() as A);
result.Add(new T2() as A);
return result;
}
You don't have to, but you can
OK, if you really want to say it returns IList<T> where T : A. Then say that!
void Main()
{
MyMethod<B>();
}
public class A {}
public class B: A {}
//public class C: A {} //Even if I don't add that class
public IList<T> MyMethod<T>()
where T : A, new()
{
var result = new List<T>();
//add some items to result
result.Add(new T());
return result;
}
Yes, that one cannot return a mix of item of type T and items of type A, because it says it returns IList<T> and not every item of type A is also an item of type T.
What happens with your code
Look at your code:
public IList<A> MyMethod(){
IList<B> result = new List<B>();
//add some items to result
return result;
}
You are trying to return an IList<B> when you said that you was going to return an IList<A>. Let's suppose that that works... then what would happen to the caller of your method? Let's see:
public class A {}
public class B: A {}
public class C: A {}
void Main()
{
//Hmmm... I need a IList<T>, let's call MyMethod!
IList<A> list = MyMethod();
//Cool, I got an IList<A>, now let's add some items...
var item = new C();
//Well, item is of type C...
// and C inherits from A, so I must be able to add it...
list.Add(item); //BOOM!
//It was actually an IList<B>!
// and C doesn't dervive from B, so you can't add it.
}
DFTBA!

Categories