调用泛型类构造函数的困境

Ale*_*ean 8 c# generics constructor initialization

我有这个通用的单例看起来像这样:

public class Cache<T>
{
    private Dictionary<Guid, T> cachedBlocks;

    // Constructors and stuff, to mention this is a singleton

    public T GetCache(Guid id)
    {
        if (!cachedBlocks.ContainsKey(id))
            cachedBlocks.Add(id, LoadFromSharePoint(id))
        return cachedBlocks[id];
    }

    public T LoadFromSharePoint(Guid id)
    {
        return new T(id)    // Here is the problem.
    }
}
Run Code Online (Sandbox Code Playgroud)

错误消息是:

无法创建类型为T的实例,因为它没有new()约束.

我必须提到我必须传递该id参数,并且没有其他方法可以这样做.任何关于如何解决这个问题的想法都将受到高度赞赏.

Dan*_*ker 14

通常,您会将类型约束为T具有默认构造函数的类型并调用它.然后,您必须添加方法或属性才能提供id实例的值.

public static T LoadFromSharePoint<T>(Guid id)
    where T : new()     // <-- Constrain to types with a default constructor
{
    T value = new T();
    value.ID = id;
    return value;
}
Run Code Online (Sandbox Code Playgroud)

或者,因为您指定必须id通过构造函数提供参数,所以可以使用反射调用参数化构造函数.您必须确保该类型定义了您要调用的构造函数.您不能将泛型类型约束为T具有除默认构造函数之外的特定构造函数的类型.(例如where T : new(Guid)不起作用.)

例如,我知道有一个构造new List<string>(int capacity)List<T>,它可以调用这样的:

var type = typeof(List<String>);
object list = Activator.CreateInstance(type, /* capacity */ 20);
Run Code Online (Sandbox Code Playgroud)

当然,您可能希望之后进行一些转换(to T).

  • @nawfal 我不赞成反思。我的第一个建议与您的基本相同,但是您在课堂上和界面上展示了它。我解释了 _"我必须传递那个 id 参数,没有其他方法可以这样做"_ 从字面上看,然后唯一的解决方案是反射,因为您必须同意。 (2认同)

naw*_*fal 5

为此,您应该指定是什么T。你Cache<T>能装什么吗?TigerFridge并且int呢?那不是声音设计。你应该约束它。您需要一个实例,T该实例将使用 aGuid来构造实例。那不是通用的T. 它是一个非常具体的T. 将您的代码更改为:

public class Cache<T> where T : Cacheable, new()
{
    private Dictionary<Guid, T> cachedBlocks;

    // Constructors and stuff, to mention this is a singleton

    public T GetCache(Guid id)
    {
        if (!cachedBlocks.ContainsKey(id))
            cachedBlocks.Add(id, LoadFromSharePoint(id))
        return cachedBlocks[id];

       //you're first checking for presence, and then adding to it
       //which does the same checking again, and then returns the
       //value of key again which will have to see for it again. 
       //Instead if its ok you can directly return

       //return cachedBlocks[id] = LoadFromSharePoint(id);

       //if your LoadFromSharePoint is not that expensive.
       //mind you this is little different from your original 
       //approach as to what it does.
    }

    public T LoadFromSharePoint(Guid id)
    {
        return new T { Key = id };    // Here is no more problem.
    }
}

public interface Cacheable
{
    Guid Key { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

现在从接口派生所有可缓存的(无论T您将传递给它的是什么Cache<T>Cacheable