2

I have a specialized generic collection class which will be used to hold collections of many different types of objects. Once the collection is created, I need to instantiate the collection's items. I am having the darnedest time getting this to work. There must be a simple solution I am missing.

Here is a sample class which kind of illustrates what I am trying to do and the warnings/errors I am bumping up against.

// Note: T may either a string or other reference type that supports IEnumerable. 
public class Foo<T>
{
    private List<T> fooBarList = new List<T>();

    public Foo()
    {
        Bar1<T>();
        Bar2<T>();
        Bar3<T>();
    }

    public void Bar1<T>()
    {
        // Error Message: Argument 1 cannot convert from 'T...' to 'T...'
        T t = default;
        fooBarList.Add(t);
    }

    public void Bar2<T>() where T : IEnumerable, new()
    {
        // Error Message: T must be a non-abstract type with public
        // parameterless constructor in order to use it as a parameter 'T'
        // in the generic type or method 'Foo<T>.Bar2<T>()

        fooBarList.Add(new T());
    }

    public void Bar3<T>() where T : IEnumerable, new()
    {
        // Error Message: Argument 1 cannot convert from 'T...' to 'T...'
        T t = Activator.CreateInstance<T>();
        fooBarList.Add(t);
    }
}

Side note: This particular code is in a particularly performance-critical part of my application--you know, the 3% Donald Knuth talks about needing to actually be optimized. This really does need to be fast because it will get called millions of times per application execution. I would not be at all enthusiastic about using reflection (e.g. Activator.CreateInstance() here) if there is any other alternative. (For now, even that does not seem to be working for me.) I would much rather have the compiler resolve the data type at compile time.

This question was already answered in the link below, but none of the approaches seem to be working for me. What am I missing?

In C#, how to instantiate a passed generic type inside a method?

FYI, I am using .NET Core 2.2 Beta and .NET Standard 2.0 on a Windows 10 machine running Visual Studio 2019 Enterprise Preview.

9
  • 2
    public void Bar1<T>()... should be public void Bar1(), these methods should not be generic but rather use the classe's generic type Commented Mar 15, 2019 at 20:58
  • I think @vc74 has the correct solution or close to it. You need to declare the where clause on T (specifying that T is a collection and is new-able) up at the class level, and then remove the <T> bits at the function level. Commented Mar 15, 2019 at 21:02
  • You've already included a restraint that the type has a default constructor when you said where T : new(), so that means you can safely do var myT = new T(); Just make sure you add that constraint wherever you need it. You can define T at the class level (and include the constraint there) if you want it to be the same everywhere. Commented Mar 15, 2019 at 21:06
  • You're getting a lot of answers below that require the concrete type of T to have a public parameterless constructor. Is this a satisfactory constraint for your purposes? Commented Mar 15, 2019 at 21:10
  • 2
    By the way, the reason for the oddball "Can't convert T to T" messages is likely because you have two T's declared, one at the class level, and one at the function level Commented Mar 15, 2019 at 21:25

2 Answers 2

2

It seems like List<T> already has all you need except a method to create a new instance and add it, which could be added as extension methods:

public static ICollectionExtensions
{
    public static AddNew<T>(this ICollection<T> collection)
        where T : new()
    {
        var newItem = new T();
        collection.Add(newItem);
    }

    ...
} 

which can be used like this:

var list = new List<int>();
list.AddNew();
Sign up to request clarification or add additional context in comments.

Comments

1

This compiles:

public class Foo<T> where T : IEnumerable, new()
{
    private List<T> fooBarList = new List<T>();

    public Foo()
    {
        Bar1();
        Bar2();
        Bar3();
    }

    public void Bar1()
    {
        T t = default(T);
        fooBarList.Add(t);
    }

    public void Bar2()
    {
        fooBarList.Add(new T());
    }

    public void Bar3() 
    {
        T t = Activator.CreateInstance<T>();
        fooBarList.Add(t);
    }
}

Note that the only declaration of T is up at the class level, both the <T> part and the where part.

8 Comments

Pretty sure this won't work unless the concrete type of T has a public, parameterless constructor.
@RobertHarvey That's what the new() constraint means...
@RobertHarvey: Yes, but that's where where T: new() comes in
In the OP's example, they are citing the error message T must be a non-abstract type with public parameterless constructor in order to use it as a parameter 'T', so I assume they want to use it with a type that isn't so ordained.
If he wants to do what his code says he wants to do (create objects, etc) then he needs a new() constraint. My point is that he can't just constrain T on the functions where he wants to do the object creation. It's the same T for the whole class, the constraint has to be at the class level
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.