字典新对象作为TValue

时间:2014-01-29 17:31:40

标签: c# generics dictionary

字典签名如下:

Dictionary<TKey, TValue>

是否有可能以某种方式将新对象实例化为TValue

我知道我可以从那里调用一个方法或new关键字,但我不能在那里有参数,我希望实例化的对象需要参数。

2 个答案:

答案 0 :(得分:4)

由于TValue没有限制是类或具有构造函数,因此没有安全的方法来“创建”实例({1}}可能是没有公共构造函数的类型!)

一种选择是使用default

TValue

将为引用类型返回var value = default(TValue); ,为数值类型返回null,或者将每个成员初始化为默认值的结构。

您还可以从0 继承并添加Dictionary具有默认构造函数的限制:

TValue

或在扩展方法中添加限制:

public class MyDict<TKey,TValue> : Dictionary<TKey, TValue> where TValue : new()
{
    public void Add(TKey key)
    {
       this.Add(key, new TValue());
    }
}

答案 1 :(得分:1)

由于你还没有澄清上下文,我想象一个内部Dictionary<TKey, TValue>存储的类和一个带有任意数量参数的工厂方法:

public class Beings<TValue>
{
    private readonly Dictionary<long, TValue> dictionary;
    private readonly Func<object[], TValue> giveBirth;

    public Beings(Func<object[], TValue> giveBirth)
    {
        this.dictionary = new Dictionary<long, TValue>();
        this.giveBirth = giveBirth;
    }

    public TValue Create(params object[] args)
    {
        var newBeing = this.giveBirth(args);
        this.dictionary[this.dictionary.Count] = newBeing;

        return newBeing;
    }
}

请注意,它的构造函数需要一个Func<object[], TValue>委托 - TValue中的任何对象。

接下来想象两个存在的案例:人类和人类克隆。人类通常有妈妈和爸爸,克隆人只有原始的人类。因此我们有类似的类:

[DebuggerDisplay("{Name}")]
public class Human
{
    public Human(string name, Human mom, Human dad)
    {
        this.Name = name;
        this.Mom = mom;
        this.Dad = dad;
    }

    public string Name { get; private set; }
    public Human Mom { get; private set; }
    public Human Dad { get; private set; }
}

public class HumanClone : Human
{
    public HumanClone(Human original)
        : base(original.Name + "_Clone", null, null)
    {
        this.Original = original;
    }

    public Human Original { get; private set; }
}

现在我们按Beings<Human>Beings<HumanClone>隔离我们的生物物种,创建具有Func<object[], TValue>签名的工厂代表:

Func<object[], Human> humanGiveBirth = humanBirthArgs =>
    new Human(name: (string)humanBirthArgs[0], mom: (Human)humanBirthArgs[1], dad: (Human)humanBirthArgs[2]);

var humans = new Beings<Human>(humanGiveBirth);

Func<object[], HumanClone> humanCloneGiveBirth = humanCloneBirthArgs =>
    new HumanClone(original: (Human)humanCloneBirthArgs[0]);

var humanClones = new Beings<HumanClone>(humanCloneGiveBirth);

为了测试我们的宇宙,我们创造了一些物种:

var human1 = humans.Create("Adam", null, null);
var human2 = humans.Create("Eva", null, null);
var human3 = humans.Create("Kain", human2, human1);
var human4 = humans.Create("Avel", human2, human1);

var humanClone1 = humanClones.Create(human1);
var humanClone2 = humanClones.Create(human2);

缺点是你没有构造函数参数的智能感知,你也必须在工厂代理定义中显式地输入每个对象arg。 也许你可以用Delegate.CreateDelegate和反思魔法来美化一些事情......

<强>更新

但是我想的越多,代码看起来越人性化......我使用TValue Create(params object[] args),但我也可以介绍TValue Create(Func<TValue> giveBirth)并使用它像

var human = humans.Create(() => new Human("Elvis", elvisMom, elvisDad));

Create方法的所有目的都只是在它喜欢的地方注入委托调用(例如在某个锁内) - 就像在ConcurrentDictionary.GetOrAdd Method中完成的那样