简体   繁体   中英

Specify generic type as argument param without knowing T

I'm approaching a problem while still having some ignorance regarding Generics and their proper declarations / uses. I get the premiss, but some of the ins-n-outs of generics still elude me.

Given the following code ( does not compile and contains code-smell ):

public interface IUIConcern<out T> where T : IUIConcernExtension
{

    string Name { get; }
    Func<T> Extend();
}

public class UIConcern
{
    public static void Register<T>(string concernName, IUIConcern<T> uiConcern) where T : IUIConcernExtension
    {
        Concerns.Add(uiConcern);
    }

    public static List<IUIConcern<T>> Concerns{
        get {
            // Logic... 
        }
        set {
            // Logic... 
        }
    }
}

... I have a few questions:

  • Why do I have to specify this part public static void Register<T>(string concernName, IUIConcern<T> uiConcern) where T : IUIConcernExtension with a constraint when I have already constrained the T in the declaration public interface IUIConcern<out T> where T : IUIConcernExtension
  • How can I have a property that holds a List<> of my IUIConcern<T> without knowing T other than knowing it will be derived from IUIConcernExtension ?

Again, I realize this doesn't compile and is not correct, just looking to see how I can hold a list of generic items that may have many different type of IUIConcern<> elements.

Thank you!

You need to have a base interface, for instance:

public interface IUIConcern
{
    string Name { get; }
}

public interface IUIConcern<out T> : IUIConcern where T : IUIConcernExtension
{
    Func<T> Extern();
}

How you would define Concerns and Register would depend on how you treat T . Alternatively if you only deal with instances where you know T , you could use a Dictionary<Type, List<IUIConcern>> to hold anything, or potentially drop the base interface and just store using object depending on what you need in your controller code.

The problem is not located at the interface, but the problem is because of your generic implementation using static methods and properties.

The answer from Guvante was correct when saying that you need to define the IUIConcernExtension , but that is of course very logical, so im assuming you have just omitted that part since it does not matter for the issue you are facing.

The problem in the code is that you have created a class that has static methods and procedures, with the generic definition not laying at class level , but at methods level , because of this, the property that has and the Method cannot assume you are always with the same type!!

Lets say you call call :

Register<string>("something", UIConcern<string>) 

but before that you have already called:

Register<Type>("something", UIConcern<Type>) 

How could the compiler allows you to that?! So the answer is to define the generic type at class level, with this all properties and methods will be of same .

Also you need to use a private member for your list, since you doing everything static, the correct code should be:

interface IUIConcernExtension
{
    string Name { get; }
}

public interface IUIConcern<out T> where T : IUIConcernExtension
{
    Func<T> Extend();
}

public class UIConcern<T> where T : IUIConcernExtension
{
    private static List<IUIConcern<T>> _Concerns = new List<IUIConcern<T>>();

    public static void Register(string concernName, IUIConcern<T> uiConcern) 
    {
        Concerns.Add(uiConcern);
    }

    public static List<IUIConcern<T>> Concerns
    {
        get { return _Concerns; }
        set { _Concerns = value; }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM