0

I would like to create an extension method to convert a ICollection of type long to a ICOllection of type long?. It would be something like that:

    public static void ConvetirLongANullableLong<T, U>(this ICollection<T> paramIcOrigenNoNullable, ICollection<U> paramIcDestinoNullable)
    {
        for (int i = 0; i < paramIcOrigenNoNullable.Count; i++)
        {
            paramIcDestinoNullable.Add((U)paramIcOrigenNoNullable.ElementAt(i));
        }
    }

But I have a problem because I can't convert U to T.

The idea was to create a generic method to convert for example long to long? in this case, but int to int? or any other non nullable basic type to a nullable type.

Is it possible or I should to create one method for each type?

This is motivated byt this post: Fastest way to convert List<int> to List<int?> that says it is better to use a foreach and don't use linq select or linq cast because it is slower.

Thanks.

PD: I give as answer to V0ldek, because it is what I was really asked in this post, but really it is faster if I use linq select, how PavelAnikhouski tells in some comment, at least using Entity Core 3.0. I don't know if in another versions is faster the for option, because the results in the link that I indicate, the times are very differnt than in the case of PavelAnikhouski and in my own case too.

Álvaro García
  • 18,114
  • 30
  • 102
  • 193
  • 3
    What is your difference to using list.Cast(), why do you need a new extension ? – Holger Jan 01 '20 at 16:55
  • I would like to use an extension method because in the link I show, one of the answers made a benchmark and linq cast is the worse performnace, for 100000 elements foreach teakes 20 second, linq select take 40 second and linq cast 77 seconds. – Álvaro García Jan 01 '20 at 17:12
  • @Holger [`Cast` boxes](https://github.com/dotnet/corefx/blob/master/src/System.Linq/src/System/Linq/Cast.cs) – V0ldek Jan 01 '20 at 17:16
  • @ÁlvaroGarcía I've the sample from linked question, `foreach` took is about 16 seconds, `Select` is about 11 and `Cast` is about 36 – Pavel Anikhouski Jan 01 '20 at 17:17
  • I'd also say that this discussion would be more suited in the linked question about these methods' performance. Here it's tangential to the core of the question, which is value types as generic type arguments. – V0ldek Jan 01 '20 at 17:19
  • @PavelAnikhouski I will check the example in my computer, but the results in the answer of the other post, the time is much higher. – Álvaro García Jan 01 '20 at 17:27
  • @PavelAnikhouski It is true, I get the same results than you, linq select is the fastest and I have similar times than you. – Álvaro García Jan 02 '20 at 12:05

3 Answers3

2

If you're gonna always convert from T to T? then, well, you don't need a type U. You know U, U = T?. But you do need to constraint T to struct.

public static void ConvertToCollectionOfNullable<T>(
    this ICollection<T> source, 
    ICollection<T?> destination) where T : struct
{
    for (int i = 0; i < source.Count; i++)
    {
        destination.Add(source.ElementAt(i));
    }
}

also, you can use foreach on a collection for cleaner code.

public static void ConvertToCollectionOfNullable<T>(
    this ICollection<T> source,
    ICollection<T?> destination) where T : struct
{
    foreach (var element in source)
    {
        destination.Add(element);
    }
}
V0ldek
  • 9,623
  • 1
  • 26
  • 57
  • @AnuViswan I'd say it depends. This way the method allows the user to convert to any target collection. You have a source `List` and want a `List`? You can use this method. You have a source `MyOwnWeirdCollection` and want a `ConcurrentQueue`? Be my guest. The obvious downside is that you require the user to construct a collection first. You can provide a reasonable default that always returns a `List` and use this one as an implementation detail inside it. – V0ldek Jan 01 '20 at 17:03
1

You can made made even simpler, using OfType<T> or Cast<T> methods from System.Linq

var list = new List<long>() { 1, 2, 3 };
var converted = list.OfType<long?>().ToList();

OfType<T> doesn't throw any exception, if cast will fail (like Cast<T>)

One more option is to use Select method for that. Since you know the result type, it won't be a problem with cast (compiler helps you too) and boxing as well

var list = new List<long>() { 1, 2, 3 };
var converted = list.Select(_ => (long?)_).ToList();
Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
  • 1
    I think there's literally no way for you to get an invalid cast by using `Cast` on a `List`, but correct me if I'm wrong. – V0ldek Jan 01 '20 at 17:05
  • 1
    `Cast` casts all elements, `OfType` filters only, without throwing an exception, depending on OP needs, I'll update an answer – Pavel Anikhouski Jan 01 '20 at 17:06
  • 1
    Note that `OfType` will box every single element, which is really quite bad. – canton7 Jan 01 '20 at 17:07
1

IEnumerable.Cast is slow, cause it uses boxing. This is what happens:

long? l = (long?)(object)8; 

But this is the only way to Cast from anything to anything. If you want it faster implement it type-specific.

But for the special case, where you want to convert a value type, to it's nullable type, you can stick with generic. You just need to avoid to handle two types (your <T, U>) it is T and T?, and you have to tell the compiler T is a value type:

  public static IEnumerable<T?> Convert<T>(this IEnumerable<T> list) where T : struct
  {
        return list.Select(x => new T?(x));
  }

if you want it faster, avoid the LINQ enumerator (avoid foreach) and use a simple loop

    public static IList<T?> Convert<T>(this IList<T> list) where T : struct
    {
        var newlist = new List<T?>(list.Count);
        for (int i = 0; i < list.Count; i++)
            newlist.Add(new T?(list[i]));
        return newlist;
    }
Holger
  • 2,446
  • 1
  • 14
  • 13
  • Actually, `new T?` in the last snippet isn't needed, you are adding the values, not converting them – Pavel Anikhouski Jan 01 '20 at 20:21
  • @PavelAnikhouski Maybe it can be done implicitly, without writing it out, but it will happen. A "new" on a struct is pretty meaningless anyway. The 'Add' is the costly thing, not the Cast. it would be faster with arrays. – Holger Jan 01 '20 at 20:27
  • Really, if I use the code in the link in the original post, with tests, I get the fastest way is with linq select, not with the for. I am using Entity core 3. I don't know with another versions could be faster the for solution. – Álvaro García Jan 02 '20 at 12:07
  • @ÁlvaroGarcía Depends also on the method you use to benchmark it. Creating an array and copying to an array would be fastest. And than creating a List with the List(IEnumerable) constructor is very fast also. You should really think about, if you do need a list at all. Do you want to append more elements after this conversion ? If not, you need no List. – Holger Jan 02 '20 at 12:23