Home > Mobile >  Return specific type in generic method depending on enum value without Reflection or dynamic during
Return specific type in generic method depending on enum value without Reflection or dynamic during

Time:11-18

I have a non-generic IList which i would like to cast depending on an enum value during runtime.

I can not change the type of the non-generic IList in the implementation, because it is library code.

Also i do not want to use Reflection (because it is slow) or the dynamic keyword (because it is unsafe and could lead to mistakes down the road).

In the Library Code there is following class:

public class ListView //THIS IS LIBRARY CODE AND CAN NOT BE CHANGED
{
  public IList itemsSource { get; set; }
}

Then in my CustomListView class which inherits from ListView i want to cast the itemsSource to the appropiate class depending on ItemType.

Another limitation is that CustomListView can not be generic (dictated by the library we use)

public class CustomListView : ListView
{
    public dynamic GetItems()
    {
        switch (ItemType)
        {
            case ItemType.A:
              return itemsSource.Cast<A>().ToList();
            case ItemType.B:
              return itemsSource.Cast<B>().ToList();
            default:
              throw new InvalidOperationException("Not supported");
        }
    }
}

But i want it to directly return the correct type instead of using dynamic.

Something like (the code below does not work!):

public IList<T> GetItems(ItemType itemType) //The type of T should change depending on what i return
{
    switch (ItemType)
    {
        case ItemType.A:
          return itemsSource.Cast<A>().ToList();
        case ItemType.B:
          return itemsSource.Cast<B>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    }
}

How would i implement that?

Edit/Appendage

As you guys pointed out i should clarify some things.

Class A and B do indeed have the same base class. But i want to be able to not cast it from the base type again (because i already cast it in the GetItems() method and also the value of ItemType is known).

I want to be able to do following

IList<A> aItems = listView.GetItems()

Without casting.

The idea behind all of this is to have a general CustomListView which can handle multiple item types. Those items will be added to the itemSource. The type of those items is determined by the ItemType.

I use it like this for example

public class UiForClassA
{
  public void Foo()
  {
    CustomListView customListView = new CustomListView(ItemType.A);

    IList<A> itemsOfCustomListView = customListView.GetItems(); //No cast needed because it should somehow implicitly know that.
  }
}

I do not want to use Casts everywhere where i use the CustomListView. It somehow should implicitly return the correct items.

And for your information i use the Unity UI Toolkit Framework, but that is not really relevant to the Question.

CodePudding user response:

Think about how this method would be used:

ItemType itemType = ...;
var x = listView.GetItems(itemType);

The value of itemType could be A, or it could be B. So what should the type of x be? It's either IList<A>, or IList<B>, and the only way to express that is to use a type that both IList<A> and IList<B> inherit from, which takes us back to IList. If you don't know the type that you want at compile time, then there is not much point trying to change the return type to something more specific, because you will not be able to access it as the more specific type without reflection.

If you do know the type that you want at compile time, then provide overloads of the method that return the desired type:

public IList<A> GetAItems() { ... }
public IList<B> GetBItems() { ... }

Edit

If you really want to use a generic method, you could do something like this:

public IList<T> GetItems<T>()
{
    return itemsSource.Cast<T>().ToList();
}

But I don't see the point in making a method for this, rather than putting that code directly where you need the specific type.

Edit 2

Having seen your use case, I really would recommend you make the CustomListView class generic. I know you say the library prevents that, but there are ways to work around this. You've made an ItemType enum, you could instead make a subclass of CustomListView<T> for each item type:

public class CustomListView<T> : ListView
{
    public IList<T> GetItems() => itemsSources.Cast<T>().ToList();
}

public class CustomListViewOfA : CustomListView<A> { }
public class CustomListViewOfB : CustomListView<B> { }

public class UiForClassA
{
    public void Foo()
    {
        var customListView = new CustomListViewOfA();
        var itemsOfCustomListView = customListView.GetItems(); //No cast needed
    }
}

CodePudding user response:

How do you expect to use such method? The type parameter is set by the caller of the method. Setting type parameter from withing the method is not possible. Please, specify the usage.

You can try these:

IList<T> GetItems<T>(ItemType itemType)
{
    switch (itemType)
    {
        case ItemType.A:
          return (IList<T>)(IList)(itemsSource.Cast<A>().ToList());
        case ItemType.B:
          return (IList<T>)(IList)(itemsSource.Cast<B>().ToList());
        default:
          throw new InvalidOperationException("Not supported");
    }
}
IList GetItems(ItemType itemType)
{
    switch (itemType)
    {
        case ItemType.A:
          return itemsSource.Cast<A>().ToList();
        case ItemType.B:
          return itemsSource.Cast<B>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    }
}

Or, as suggested by @Qwertyluk in comments, something like this:

private IList<object> GetItems(ItemType itemType)
{
    
    switch (itemType)
    {
        case ItemType.A:
        case ItemType.B:
          return itemsSource.Cast<object>().ToList();
        default:
          throw new InvalidOperationException("Not supported");
    }
}
  • Related