Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cast abstract class into type T?

Tags:

c#

I have an abstract class:

abstract class A {
    // some stuff
    float val = 0;
    abstract float Foo();
}

class B1 : A {
    override float Foo() {
        Console.WriteLine("B1: " + val);
    }
}

class B2 : A {
    override float Foo() {
        Console.WriteLine("B2: " + val);
    }
}

and I have a List<A> a and I want to get a first item of given type T:

public T GetTypeFromList<T>() {
    foreach (var item in a) {
        T tmp = item as T; // doesn't compile
        if (tmp != null)
            return tmp;
    }
    throw new Exception("Type isn't in list.");
}

Is there a way to do it?
Edit:
It shows: The type parameter 'T' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' constraint

like image 463
galaxy001 Avatar asked Dec 28 '19 15:12

galaxy001


2 Answers

Don't use as for checking the type. Use is instead:

if (item is T)
    return (T)item;

Here is the detailed explanation about Type casting in msdn.

like image 95
CoderCharmander Avatar answered Sep 27 '22 15:09

CoderCharmander


For making your code to work you have to add where constraint to your method:

static T GetTypeFromList<T>(List<A> a) where T: class and then it will work.

Because, as you are using as keyword, it converts the object to a given reference or nullable value type. In case of failed conversion the result of as operator will be null. And null is valid only for nullable types. For satisfying the compiler you have to add class constraint to your method signature.

But, there is already built-in method exits in System.Linq namespace for that purpose. It is called OfType<>. It filters the elements of an IEnumerable based on a specified type.

var b2s = list.OfType<B2>();

And for getting the first item you can make use of First() or FirstOrDefault() which will return null if such element is not exist in the collection:

var b2 = list.OfType<B2>().FirstOrDefault(); // or First()

By the way, if you will take a look to the implementation of OfType<> you will see that it returns OfTypeIterator and inside that method it just iterates over the collection and uses is operator to find out the objects with desired type:

 static IEnumerable<TResult> OfTypeIterator<TResult>(IEnumerable source) {
            foreach (object obj in source) {
                if (obj is TResult) yield return (TResult)obj;
            }
        }

At the end would like to note, using as keyword will give better performance in that situation. But in case OfType<>, is operator is used at first to check the type and then conversion will happen. So, if performance is important for you, you can continue to use your own implementation.

like image 26
Farhad Jabiyev Avatar answered Sep 27 '22 16:09

Farhad Jabiyev