我需要为整个项目中的常规树提供一个类。有时树与字符串键一起使用,有时与 Guid 键一起使用。 该类不应具有 null Id 和可为 null 的 ParentId。
public class TreeNode<TKey>
{
public TKey Id { get; set; }
public string Name { get; set; }
public TKey? ParentId { get; set; }
}
1.a. TKey = 指南。当尝试将 null 值分配给 ParentId 时,出现错误“CS0037:无法将 null 转换为“类型”,因为它是不可为 null 的值类型”。
1.b. TKey = 字符串。字符串键没有问题。
where TKey: struct
:public class TreeNode<TKey> where TKey : struct
{
public TKey Id { get; set; }
public string Name { get; set; }
public TKey? ParentId { get; set; }
}
2.a. TKey = 指南。没有任何错误。
2.b. TKey = 字符串。我遇到错误“CS0453:类型‘类型名称’必须是不可为 null 的值类型,才能将其用作泛型类型或方法‘通用标识符’中的参数‘参数名称’”。
如何修改类以便可以使用两种类型的密钥?
主要问题是
Guid
是值类型,而 string
是引用类型。
当我们创造性地对其应用约束时,拥有值或引用类型的通用参数并没有给我们很大的操作空间(docs)。
我可以给你的解决方案是声明一个封装引用类型的结构泛型类型,如下所示:
public struct StructOf<TClass>
where TClass : class {
public TClass Instance;
public StructOf(TClass instance) => Instance = instance;
public static implicit operator StructOf<TClass>(TClass s) => new StructOf<TClass>(s);
public static implicit operator TClass(StructOf<TClass> s) => s.Instance;
public override string ToString() {
return this.Instance.ToString();
}
// TODO: override Equals, GetHashCode etc., ==, !=
}
现在,您的原始泛型类型可以像这样受到约束:
public class TreeNode<TKey>
where TKey : struct {
public TKey Id { get; set; }
public string Name { get; set; }
public TKey? ParentId { get; set; }
}
因此示例用法将是这样的(通过重载隐式运算符使操作变得更简单)
StructOf<string> foo = "foo"; // possible via implicit operator overload
Console.WriteLine(foo); // foo
string bar = foo; // possible via implicit operator overload
Console.WriteLine(bar); // foo
TreeNode<StructOf<string>> treeNode = new();
treeNode.Id = "foo"; // again implicit operator
treeNode.ParentId = null;
TreeNode<Guid> baz = new ();
baz.Id = new Guid();
baz.ParentId = null;