Repository navigation
Open
Description
This is built-in and can be generated through 'SourceGenerator'.
[Union(0, typeof(Test1))]
[Union(1, typeof(Test2))]
public abstract class Test<T>
{
[Key(0)]
public Dictionary<string, T> Children { get; set; }
}
[MessagePackObject]
public class Test1 : Test<string>
{
}
[MessagePackObject]
public class Test2 : Test<Color>
{
}
Unfortunately, this approach does not have the capability to generate code.
[Union(0, typeof(Test1))]
[Union(1, typeof(Test2))]
public abstract class Test<T>
{
[Key(0)]
[MessagePackFormatter(typeof(CustomDictionaryFormatter<,>))] //Not working
[MessagePackFormatter(typeof(CustomDictionaryFormatter<string,T>))] //Not working
public CustomDictionary<string, T> Children { get; set; }
}
[MessagePackObject]
public class Test1 : Test<string>
{
}
[MessagePackObject]
public class Test2 : Test<Color>
{
}
public sealed class CustomDictionaryFormatter<TKey, TValue> : DictionaryFormatterBase<TKey, TValue, CustomDictionary<TKey, TValue>, CustomDictionary<TKey, TValue>.Enumerator, CustomDictionary<TKey, TValue>> where TKey : notnull
{
protected override void Add(CustomDictionary<TKey, TValue> collection, int index, TKey key, TValue value, MessagePackSerializerOptions options)
{
collection.Add(key, value);
}
protected override CustomDictionary<TKey, TValue> Complete(CustomDictionary<TKey, TValue> intermediateCollection)
{
return intermediateCollection;
}
protected override CustomDictionary<TKey, TValue> Create(int count, MessagePackSerializerOptions options)
{
return new CustomDictionary<TKey, TValue>(count, options.Security.GetEqualityComparer<TKey>());
}
protected override CustomDictionary<TKey, TValue>.Enumerator GetSourceEnumerator(CustomDictionary<TKey, TValue> source)
{
return source.GetEnumerator();
}
}
While it is the only way to generate the code correctly, it is quite inconvenient. I was wondering if there are any better alternatives available?
[Union(0, typeof(Test1))]
[Union(1, typeof(Test2))]
public abstract class Test<T>
{
[IgnoreMember]
public abstract CustomDictionary<string, T> Children { get; set; }
}
[MessagePackObject]
public class Test1 : Test<string>
{
[Key(0)]
[MessagePackFormatter(typeof(CustomDictionaryFormatter<string, string>))]
public override CustomDictionary<string, string> Children { get; set; }
}
[MessagePackObject]
public class Test2 : Test<Color>
{
[Key(0)]
[MessagePackFormatter(typeof(CustomDictionaryFormatter<string, Color>))]
public override CustomDictionary<string, Color> Children { get; set; }
}
public sealed class CustomDictionaryFormatter<TKey, TValue> : DictionaryFormatterBase<TKey, TValue, CustomDictionary<TKey, TValue>, CustomDictionary<TKey, TValue>.Enumerator, CustomDictionary<TKey, TValue>> where TKey : notnull
{
protected override void Add(CustomDictionary<TKey, TValue> collection, int index, TKey key, TValue value, MessagePackSerializerOptions options)
{
collection.Add(key, value);
}
protected override CustomDictionary<TKey, TValue> Complete(CustomDictionary<TKey, TValue> intermediateCollection)
{
return intermediateCollection;
}
protected override CustomDictionary<TKey, TValue> Create(int count, MessagePackSerializerOptions options)
{
return new CustomDictionary<TKey, TValue>(count, options.Security.GetEqualityComparer<TKey>());
}
protected override CustomDictionary<TKey, TValue>.Enumerator GetSourceEnumerator(CustomDictionary<TKey, TValue> source)
{
return source.GetEnumerator();
}
}
Thank you very much for your patience and attention. I would greatly appreciate any suggestions or alternative methods you may have.
Metadata
Metadata
Assignees
Labels
No labels
Activity
[-]How to use "SourceGenerator" to support generic custom formatters?[/-][+]Requesting Assistance:Seeking Guidance on Utilizing "SourceGenerator" for Generic Custom Formatters.[/+]AArnott commentedon Jun 23, 2023
True, we don't support multiple formatter attributes on the same member today AFAIK. Or are you saying that either one works when you try them one-at-a-time?
If one-at-a-time, I could see that being likely solvable. But it may not be trivial. If you're willing to author a PR that includes tests, I'd be happy to review it.
Sponsorship may be an effective way to lead a contributor of this repo to prioritize the feature. As a free and open source library, all or the majority of its development is done without any monetary benefit to its authors, and all of it is made available free of charge. But sponsorship is very much appreciated and, with agreement from a particular contributor and/or the owning team, can be a great way to support the project and help get your favorite features or bugs addressed in an upcoming version.
If sponsorship to drive completion of a feature or bug fix is an interesting option to you, please let us know. You can reach out to me privately on keybase.io or gitter.im as well.
WildMeowGames commentedon Jun 24, 2023
Thank you for your reply. I'm sorry if I didn't make myself clear.
What I meant was that in the first code snippet, a normal Dictionary is used in the abstract class Test. By specifying a key (0), the SourceGenerator can generate AOT code for all subclasses without any issues.
However, in the second and third code snippets, a CustomDictionary<T,V> from an external DLL library is used. To accommodate this, I implemented a Formatter<T,V>. Unfortunately, when attempting to use the MessagePackFormatter, I encountered a compilation error because it doesn't support generic parameters. Hence, I provided the third code snippet, which is the only approach that allows the SourceGenerator to generate code correctly. However, this approach is quite cumbersome.
What I want to figure out is how to properly use the SourceGenerator to generate code after I have implemented a custom formatter for an external generic class.
AArnott commentedon Aug 13, 2023
So your goal is to use the source generator for most types, but to skip over one of them because you'll take care of it manually? If so, that's documented in the README file. Although the format by which you do it is about to change in #1667 so you may want to look at the README file as it is found there (then you can decide whether to follow the before or after change doc based on the version of messagepack you're using).