5

I need to create a list starting from an array already created immediately before and will only be converted to the list. So I could harness the array to the list without making a copy, but the constructor makes a copy. I can even understand the motivation for this. However there are cases that I can guarantee that the array does not have and will have no reference to it other than where it was created from.

Is there any way to make this construction more efficient and use the array internally in the list? I know there are implications if I misuse it.

The most obvious example for this is to get the result of a string.Split(). If you need a list your only obvious way out would be to do this conversion. For now I'm not considering writing a method to split directly into a list.

22
  • Try : string.Split().ToList() so you do not create the array. Commented Jan 24, 2019 at 18:32
  • @jdweng It creates an array and converts to a List. Commented Jan 24, 2019 at 18:41
  • Yes, but it doesn't create a variable. So later in your code you don't have to create the list as a 2nd variable. Commented Jan 24, 2019 at 18:52
  • I think you cannot avoid the copy of an array. Look at stackoverflow.com/questions/54298050/…. Commented Jan 24, 2019 at 19:06
  • @jdweng my problem is not the variable, is the object. Commented Jan 24, 2019 at 19:10

2 Answers 2

2

As far as I know, there is no official way to do that but it is still possible using System.Reflection. By looking at the source code of the List<T>, .NET Framework 4.7.2, the two important properties are _items and _size. There is also _version but that one changes only when you modify List<T>. Modification are Add, AddRange, Remove, etc. but also Reverse and Sort. So let's assume this is the same operation as creating the list from IEnumerable<T> where _version stays zero.

public static class ListExtensions
{
    public static void SetUnderlyingArray<T>(this List<T> list, T[] array)
    {
        lock (list)
        {
            SetInternalArray(list, array);
            SetInternalArraySize(list, array.Length);
        }
    }

    private static void SetInternalArraySize<T>(this List<T> list, int size)
    {
        var prop = list.GetType().GetField(
            "_size", 
            BindingFlags.NonPublic | BindingFlags.Instance);
        prop.SetValue(list, size);
    }

    private static void SetInternalArray<T>(this List<T> list, T[] array)
    {
        var prop = list.GetType().GetField(
            "_items",
            BindingFlags.NonPublic | BindingFlags.Instance);
        prop.SetValue(list, array);
    }
}

and then set the underlying array

int[] array = Enumerable.Repeat(1, 1000000).ToArray();
List<int> list = new List<int>();

list.SetUnderlyingArray(array);

Note This solution is highly dependent on the details of the implementation and might be wrong if something change in the List<T> internals but it gives insight on how it could be accomplished.

Sign up to request clarification or add additional context in comments.

2 Comments

I will try. I don´t want this solution exactly because it is detail dependent. But probably this is the only option. If _items were protected it would be ease.
@Maniero Exactly. I stated that dependency I am aware of that and I do understand your concern...
1

If you don't specifically need a List<T> You could create a new class that implements IList<T> and doesn't make a copy.

3 Comments

This is an option if everything fails, I thought about that, it-s a radical way, but it's a valid one.
I'd say it's less radical than a reflection approach. There's no dependency on the implementation of List<T>
Yep, reflection is radical too

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.