Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Populating generic object that is known to be an array at runtime

Tags:

c#

.net

.net-8.0

I have a generic method that returns type T (generic). In the method, if T is an array, I want to create and populate this array with data and return.

I've gotten to the point where I have a List<object?> of values and these objects are all of the type of the array elements of T (i.e. I have verified T is an array, and I have found the base type for the elements in that array, and I now have a list of objects of that base type).

What I can't figure out is how to cast the list into an array, with its type being T. I could build an array with reflection, but I still don't know how to cast that into the generic, even when they will be the exact same type.

I know this is a weird architectural design, but it is solving a complex issue.

Below is some simplified example code of what I am trying to do. The line indicated will not compile ("Cannot convert type 'object?[]' to 'T'") but is ultimately what I want to accomplish.

static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");

    Testing test = new Testing();
    DataClass[] output = test.MyMethod<DataClass[]>();
}

public class DataClass
{
    public int I = 0;
    public DataClass()
    {

    }
}

public class Testing
{
    public T MyMethod<T>()
    {
        T result = default(T);
        if (typeof(T).IsArray)
        {
            Type? baseType = typeof(T).GetInterfaces()
                .Where(o => o.IsGenericType
                    && o.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IEnumerable<>))
                .Select(o => o.GetGenericArguments()[0]).FirstOrDefault();
            if (baseType != null)
            {
                List<object?> items = new List<object?>();
                for (int i = 0; i < 5; i++)
                {
                    // Just example code here to populate the array...
                    items.Add(Activator.CreateInstance(baseType));
                }

                result = (T)items.ToArray(); // <=== This is what I need to do but doesn't compile
            }
        }
        return result;
    }
}
like image 748
GafferMan2112 Avatar asked Dec 22 '25 22:12

GafferMan2112


1 Answers

Ideally you would have a separate function to create these arrays. This function would have a generic parameter of the element type of the array.

Then if necessary you can use reflection to run the function.

public T MyMethod<T>() where T : new()
{
    T result = default(T);
    if (typeof(T).IsArray)
    {
        var baseType = typeof(T).GetElementType();
        result = (T)
            this.GetType().GetMethod(nameof(CreateArray))
            .MakeGenericMethod(baseType)
            .Invoke();
    }
    else
        // etc

    return result;
}

public T[] CreateArray<T>() where T : new()
{
    var result = new T[5];
    for (int i = 0; i < result.Length; i++)
        result[i] = new();

    return result;
}

If it's slow then you can cache a CreateDelegate invoker in a Dictionary.

Dictionary<Type, Delegate> _delegates = new();

public T MyMethod<T>() where T : new()
{
    T result = default(T);
    if (typeof(T).IsArray)
    {
        if (!_delegates.TryGetValue(typeof(T), out var dlg) || dlg is not Func<T> invoker)
        {
            var baseType = typeof(T).GetElementType();
            invoker =
                this.GetType().GetMethod(nameof(CreateArray))
                .MakeGenericMethod(baseType)
                .CreateDelegate(typeof(Func<T>));
            _delegates[typeof(T)] = invoker;
        }

        result = invoker();
    }
    else
        // etc

    return result;
}
like image 75
Charlieface Avatar answered Dec 24 '25 10:12

Charlieface



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!