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

时间:2013-02-21 13:05:13

标签: c# constructor initialization generics

我有这个通用单例,如下所示:

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.
    }
}

错误消息是:

  

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

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

3 个答案:

答案 0 :(得分: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;
}

或者,因为您指定 通过构造函数提供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);

当然,您可能希望之后进行一些投射(T)。

答案 1 :(得分:4)

为此,您应指定T是什么。你的Cache<T>能举办什么吗? TigerFridgeint也是如此?那不是一个合理的设计。你应该约束它。您需要一个T的实例,它将使用Guid来构造实例。这不是通用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; }
}

现在从界面T派生所有可缓存的(可以为Cache<T>传递的Cacheable)。

答案 2 :(得分:0)

为了在没有任何约束的情况下使用Generic Type的构造函数,并且在类中,需要使用语法,其中T:class,new()

这可以根据使用的目标类在运行时更改属性(字段)的值 - 不仅是获取/设置属性

首先,声明泛型类:

public class Foo<T>   where T : class, new()
{
    public T oneEmptyElement()
    {
        return new T();
    }

    public T setAttribute(string attributeName, string attributeValue)
    {
        T objT = new T();
        System.Reflection.FieldInfo fld = typeof(T).GetField(attributeName);
        if (fld != null)
        {
            fld.SetValue(objT, attributeValue);
        }
        return objT;
    }

    public List<T> listOfTwoEmptyElements()
    {
        List<T> aList = new List<T>();
        aList.Add(new T());
        aList.Add(new T());
        return aList;
    }
}

然后声明一个潜在的目标类:

public class Book
{
    public int name;
}

最后,呼叫可以像这样完成:

        Foo<Book> fooObj = new Foo<Book>();

        Book aBook = fooObj.oneEmptyElement();
        aBook.name = "Emma";

        Book anotherBook = fooObj.setAttribute("name", "John");

        List<Book> aListOfBooks = fooObj.listOfTwoEmptyElements();
        aListOfBooks[0].name = "Mike";
        aListOfBooks[1].name = "Angelina";

        Console.WriteLine(aBook.name);    //Output Emma
        Console.WriteLine(anotherBook.name);    //Output John
        Console.WriteLine(aListOfBooks[0].name); // Output Mike
        Console.WriteLine(aListOfBooks[1].name);  // Output Angelina
相关问题