Visitar URL original
Requesting Assistance:Seeking Guidance on Utilizing "SourceGenerator" for Generic Custom Formatters. · Issue #1646 · MessagePack-CSharp/MessagePack-CSharp · GitHub
Skip to content

Requesting Assistance:Seeking Guidance on Utilizing "SourceGenerator" for Generic Custom Formatters. #1646

Open

Description

@WildMeowGames

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.

Activity

  1. changed the title [-]How to use "SourceGenerator" to support generic custom formatters?[/-] [+]Requesting Assistance:Seeking Guidance on Utilizing "SourceGenerator" for Generic Custom Formatters.[/+] on Jun 19, 2023
  2. AArnott commented on Jun 23, 2023

    @AArnott
    Collaborator

    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.

  3. WildMeowGames commented on Jun 24, 2023

    @WildMeowGames
    Author

    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?

    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.

  4. AArnott commented on Aug 13, 2023

    @AArnott
    Collaborator

    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.

    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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions