Open jhf opened 2 years ago
Have you considered using type inheritance for that?
public abstract record BaseAgent;
public record MonsterAgent: BaseAgent
{
public Monster Monster { get; set; }
}
public record PlayerAgent : BaseAgent
{
public Player Player { get; set; }
}
class SomeType
{
public BaseAgent Agent { get; set; }
}
then you can write something like that:
void DoSomethingFor(SomeType t)
{
switch (t.Agent)
{
case MonsterAgent monsterAgent:
break;
case PlayerAgent playerAgent:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
Yes, for C# only that would be nice, however I'm exposing this as types to other programming languages, that don't support abstract types, but does support union/sum types, thats why I'm using composition that can always be translated to a simple dto structure.
Sorry for the late response, I've been thinking a lot about this problem.
I could try to implement an analyzer that handles the following syntax
public record AgentUnion {
[FieldUnion(Group="Group1")]
public Monster? Monster {get; set;}
[FieldUnion(Group="Group1")]
public Player? Player {get; set;}
}
public record Monster {
public int ArtificialIntelligenceLevel {get; set;}
}
public record Player {
public List<string> BackpackContents {get; set;}
}
.... but I'm afraid that this might introduce too much magic. @jhf what do you think about it? What should I report /check when properties are marked with this attribute?
@cezarypiatek Sorry for the late reply as well.
While the mechanism you describe is the ultimate in flexibility, I think a more direct approach would be simpler.
In particular, the simplest, and for me most frequently required usage, would be with an anonymous Group
where inventing a name would be strange.
In that case I would have loved to just have a class/record attribute FieldUnion
that would require all attributes to be optional and one would always be required.
@jhf I might want to take a look at this project https://github.com/StefH/AnyOf
@cezarypiatek I have been using OneOf, but I see that AnyOf
has support for json as well!
In the case of AnyOf
then the type would be quite straightforward:
...
public AnyOf<Monster,Player> Agent {get; set;}
...
I'm currently using ServiceStack.OrmLite so I would need that to understand the AnyOf as well.
The OneOf
has the advantage that it checks for completeness at compile time.
I have a project where we are simulating union types, by having a class where one, and only one, attribute can and must be set.
For instance (contrived example):
This allows a heterogenous list of either player or monster. It would have been great to have an annotation like
[OneOf]
or[Union]
that requires the user to provide one, and only one, of those attributes.And, yes, there is no good way of handling each one of these cases in C#, with a compiler check, and I'm not sure what that would look like.
I'm using: