我想在 C# 中实现以下内容:
Key
的类,将用于唯一标识一个对象using System.Text.Json;
public sealed class Test
{
private static readonly HashSet<string> _existingKeys = [];
public static readonly Test One = new("one");
public static readonly Test Two = new("two");
public Test(string key)
{
if (!_existingKeys.Add(key))
{
throw new ArgumentException($"Key '{key}' is already in use.");
}
Key = key;
}
public string Key { get; }
}
// Example usage
class Program
{
static void Main()
{
try
{
var three = new Test("one");
}
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message); // Output: Key 'one' is already in use.
}
var one = JsonSerializer.Serialize(Test.One);
try
{
var deserialized = JsonSerializer.Deserialize<Test>(one);
}
catch (ArgumentException ex)
{
Console.WriteLine(ex.Message); // Output: Key 'one' is already in use.
}
}
}
我面临的问题是反序列化,因为它尝试实例化内存中可能已存在的对象。有解决方法吗?我想避免手动维护键映射,因为某些类将有很多预定义的对象,并且很容易错过对象。此外,在某些用例中,需要在另一个程序集中的定义类之外定义对象。
请注意,我只关心用户定义对象的唯一性。
public static readonly Test One = new("one");
诸如反序列化/反射之类的事情超出了范围。
这看起来像是 Flyweight 模式的变体,因此您可能需要编辑该类,使其看起来像这样:
[JsonConverter(typeof(TestJsonConverter))]
public sealed class Test
{
private static readonly Dictionary<string, Test> _existingObjects = new();
public static readonly Test One = Create("one");
public static readonly Test Two = Create("two");
private Test(string key)
{
Key = key;
}
public static Test Create(string key)
{
if (_existingObjects.TryGetValue(key, out var existing))
return existing;
var newObject = new Test(key);
_existingObjects[key] = newObject;
return newObject;
}
public string Key { get; }
}
请注意,构造函数现在是
private
,因此所有客户端代码都必须使用 Create
函数。 static
字段 One
和 Two
也可以执行此操作。
它不是维护一组已知 ID,而是维护已知对象的哈希图。
现在通过键创建新对象永远不会失败,而只是在哈希图中查找已知对象后。
JSON 序列化是棘手的部分,但正如 shingo 的评论所建议的那样你可以使用自定义转换器:
internal class TestJsonConverter : JsonConverter<Test>
{
public override Test? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
var key = reader.GetString();
if (key is null)
return null;
return Test.Create(key);
}
public override void Write(
Utf8JsonWriter writer,
Test value,
JsonSerializerOptions options)
{
writer.WriteStringValue(value.Key);
}
}
这只是一个概念证明,您可能需要根据您的特定要求进行修改。
这些测试现已通过:
[Fact]
public void CreateOneByKey()
{
var actual = Test.Create("one");
Assert.Equal(Test.One, actual);
}
[Fact]
public void RoundTripOne()
{
var one = JsonSerializer.Serialize(Test.One);
var actual = JsonSerializer.Deserialize<Test>(one);
Assert.Equal(Test.One, actual);
}