0

Consider the following code:

public interface ITest
{
    public string Id { get; set; }
}

public class MyObject : ITest {
{
    public string Id { get; set; }
    public string Value { get; set; }
}

public void Run()
{
    IList<MyOBject> myObjects = GetMyObjects();

    myObjects = GetTestITems(myObjects);
}

public IList<ITest> GetTestItems(IList<ITest> items)
{
    //run some logic
    return items;
}

The above code will fail twice. The first time is when it tries to cast IList<MyObject> to IList<ITest> and then when it tries to cast back to IList<MyObject>. However, if I change the code to following, then it works:

public void Run()
{
    IList<MyOBject> myObjects = GetMyObjects();
    var myTests = new List<ITest>();

    foreach(var o in myObjects) {
        myTests.Add(o);
    }

    var result = GetTestITems(myTests);
    myObjects = new List<MyObject>();

    foreach(var r in result) {
        myObjects.Add(r);
    }
}

Is there a way I can achieve this without having to loop twice like this? Maybe a linq way or something in form of casting? I have tried direct casts but it failed on both instances, going to ITest and back to MyObject.

Bagzli
  • 6,254
  • 17
  • 80
  • 163
  • Can you use IEnumerable instead of IList? – user1781290 Jul 14 '19 at 17:58
  • @user1781290 on which part? – Bagzli Jul 14 '19 at 18:00
  • If you can change public IList GetTestItems(IList items) to public IEnumerable GetTestItems(IEnumerable items) you should be fine. If not, you need to create a new list – user1781290 Jul 14 '19 at 18:01
  • @user1781290 I tried changing but then visual studio complains it can't cast it. – Bagzli Jul 14 '19 at 18:04
  • 1
    If you can use `IReadOnlyList` as the parameter for `GetTestItems()`, that would address half of your concern. The other half is intractable, for reasons explained in the numerous existing Stack Overflow posts involving type parameter variance, including the marked duplicate (which has some of the more extensive explanations on the site). It simply is not type-safe to cast back even from `IReadOnlyList`, never mind `IList`, when the type parameters `T` are different. – Peter Duniho Jul 14 '19 at 18:28
  • 1
    Agreeing with @PeterDuniho. Use `IReadOnlyList` which is covariant ("out") in `T` to get the safe conversion for free. The unsafe way you cannot do. If you insist, you can do `.Cast()` as a different syntax, but it still traverses the list, and it will blow up when/if you encounter an `ITest` which happens to not be a `MyObject`. – Jeppe Stig Nielsen Jul 14 '19 at 18:33

1 Answers1

1

I think that you need to use Generics in this case, something like this:

public interface ITest
{
    public string Id { get; set; }
}

public class MyObject : ITest {
{
    public string Id { get; set; }
    public string Value { get; set; }
}

public void Run()
{
    List<MyOBject> myObjects = new List<MyOBject>(); // With your objects

    myObjects = GetTestItems<MyOBject>(myObjects);
}

public IList<T> GetTestItems<T>(IList<T> items)
where T : ITest
{
    //run some logic with props of ITest
    return items;
}
Hamlet Leon
  • 427
  • 3
  • 9
  • You don't have to specify the type in the `GetTestItems` call as it will be inferred from passing `myObjects` as the `items` argument. – juharr Jul 14 '19 at 20:57