Currently creating a new Bencodex.Types.Dictionary
instance requires lines of code, e.g.:
new Bencodex.Types.Dictionary(new Dictionary<Bencodex.Types.IKey, Bencodex.Types.IValue>
{
[(Text) "foo"] = (Text) "bar",
[(Text) "baz"] = new Binary(Guid.NewGuid().ToByteArray()),
[(Text) "qux"] = (Integer) 123,
})
As it involves unnecessary elements like casting and immediate objects, I suggest to have a high-level โbuilderโ API. The following are example code I propose. Please leave comments which is better and why, or your own proposals would be great too.
Proposal 1. Overloading SetItem(string, T)
Although Bencodex.Types.Dictionary
already has SetItem(IKey, IValue)
method as it implements IImmutableDictionary<IKey, IValue>
, SetItem()
is not enough convenient to build a long dictionary:
(Bencodex.Types.Dictionary) new Bencodex.Types.Dictionary()
.SetItem((Text) "foo", (Text) "bar")
.SetItem((Text) "baz", new Binary(Guid.NewGuid().ToByteArray()))
.SetItem((Text) "qux", (Integer) 123)
The problem of the above code is that you still need a lot of casting operators. As the return type of SetItem()
is not Bencodex.Types.Dictionary
but IImmutableDictionary<IKey, IValue>
, you need to cast the result to Bencodex.Types.Dictionary
again.
In order to work around this inconvenience, I suggest to overload SetItem(string, T)
which returns Bencodex.Types.Dictionary
(where T
can be frequently used types like string
or int
). With such overloading, code can be more concise like:
new Bencodex.Types.Dictionary()
.SetItem("foo", "bar")
.SetItem("baz", Guid.NewGuid().ToByteArray())
.SetItem("qux", 123)
(The above code assumes there are 3 overloads: SetItem(string, string)
, SetItem(string, byte[])
, and SetItem(string, int)
).
One of the cons is that the method name SetItem
still needs to be repeated.
Proposal 2. ToBencodexDictionary(this IDictionary<string, IValue>)
extension
The second suggestion is an extension method to convert an ordinary mutable dictionary into Bencodex.Types.Dictionary
, in order to leverage C#'s collection initializer, which is unavailable to immutable objects at the moment (this syntax relies on mutability: Add()
method).
The idea is simple: you build a mutable dictionary, and make it Bencodex.Types.Dictionary
by freezing it. And for convenience, all string
keys are converted to Text
keys. It would look like:
new Dictionary<string, IValue>
{
["foo"] = (Text) "bar",
["baz"] = new Binary(Guid.NewGuid().ToByteArray()),
["qux"] = (Integer) 123,
}.ToBencodexDictionary()
A con here is that it still requires values to be converted to IValue
.
Proposal 3. Declarative serializer using attributes
The last proposal is the most ambitious and the most difficult to implement at a time. It's basically similar approach to .NET's standard serialization and NewtonSoft.Json; we provide declarative attributes, and attributed types automagically became serialized to Bencodex.Types.Dictionary
:
using Bencodex.Declarative;
[DictionaryEncoded]
class Record
{
[DictionaryKeyEncoded("foo")]
public string Foo { get; set; }
[DictionaryKeyEncoded("baz")]
public Guid Baz { get; set; }
[DictionaryKeyEncoded("qux")]
public int Qux { get; set; }
}
new Encoder().Encode(
new Record
{
Foo = "bar",
Baz = Guid.NewGuid(),
Qux = 123,
}
)
This approach has many pros and cons at a time: it gets rid of a lot of boilerplate code; the attributes can be used for deserialization as well; it can be extended to Bencodex.Types.List
or any other Bencodex types as well as Bencodex.Types.Dictionary
; it takes time to implement; it involves runtime reflectionโฆ