C
C#2d ago
Lisa

✅ Nested Record Inheritance

I'm trying a bit of inheritance shenanigans, the API I'm working with wraps everything in a data object, which usually has two fields that always appear:
public abstract record CharacterActionResult([property: JsonPropertyName("data")] CharacterActionResultData Data);

public abstract record CharacterActionResultData(
[property: JsonPropertyName("cooldown")] Cooldown Cooldown,
[property: JsonPropertyName("character")] Character Character
);
public abstract record CharacterActionResult([property: JsonPropertyName("data")] CharacterActionResultData Data);

public abstract record CharacterActionResultData(
[property: JsonPropertyName("cooldown")] Cooldown Cooldown,
[property: JsonPropertyName("character")] Character Character
);
This way I can keep a base record for when I need to access cooldown/character in a generic method, but I can still the define the fields that are different:
public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
[property: JsonPropertyName("fight")] Fight Fight,
Cooldown Cooldown,
Character Character
) : CharacterActionResultData(Cooldown, Character);
public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
[property: JsonPropertyName("fight")] Fight Fight,
Cooldown Cooldown,
Character Character
) : CharacterActionResultData(Cooldown, Character);
However, I get an error: 0>FightResult.cs(6,43): Error CS8866 : Record member 'Artifacts.Model.Characters.CharacterActionResult.Data' must be a readable instance property or field of type 'Artifacts.Model.Characters.FightResultData' to match positional parameter 'Data'. Am I looking over something?
13 Replies
MODiX
MODiX2d ago
Compile C# code in #bot-spam, use !eval Example:
!eval for(int i = 0; i < 4; i++) Console.WriteLine(i);
!eval for(int i = 0; i < 4; i++) Console.WriteLine(i);
MODiX
MODiX2d ago
Lisa
REPL Result: Failure
public abstract record CharacterActionResult(CharacterActionResultData Data);

public abstract record CharacterActionResultData(
DateTime Cooldown,
string Character
);

public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
Guid Fight,
DateTime Cooldown,
string Character
) : CharacterActionResultData(Cooldown, Character);
public abstract record CharacterActionResult(CharacterActionResultData Data);

public abstract record CharacterActionResultData(
DateTime Cooldown,
string Character
);

public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
Guid Fight,
DateTime Cooldown,
string Character
) : CharacterActionResultData(Cooldown, Character);
Exception: CompilationErrorException
- Record member 'CharacterActionResult.Data' must be a readable instance property or field of type 'FightResultData' to match positional parameter 'Data'.
- Record member 'CharacterActionResult.Data' must be a readable instance property or field of type 'FightResultData' to match positional parameter 'Data'.
Quoted by
<@100644656710180864> from #bot-spam (click here)
Compile: 345.580ms | Execution: 0.000ms | React with ❌ to remove this embed.
333fred
333fred2d ago
What do you actually want to have happen here?
Lisa
LisaOP2d ago
I mostly just want a contract so I can access Cooldown/Character across the different result types And if that reduces some duplication, that's a bonus
333fred
333fred2d ago
No, as in literally what do you want to happen? What properties? What are their types?
Lisa
LisaOP2d ago
public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
[property: JsonPropertyName("fight")] Fight Fight,
Cooldown Cooldown, // This one
Character Character // This one
) : CharacterActionResultData(Cooldown, Character);
public record FightResult(FightResultData Data) : CharacterActionResult(Data);

public record FightResultData(
[property: JsonPropertyName("fight")] Fight Fight,
Cooldown Cooldown, // This one
Character Character // This one
) : CharacterActionResultData(Cooldown, Character);
So I can
CharacterActionResult someResult = GetResult<T>();
someResult.Data.Cooldown;
someResult.Data.Character;
CharacterActionResult someResult = GetResult<T>();
someResult.Data.Cooldown;
someResult.Data.Character;
many things
many things2d ago
records can't do that, either you use a class to being able to declare new/override or you don't use inheritance, i'm afraid
Lisa
LisaOP2d ago
:salute2: Throwing it into a class then, Couldn't find that limitation in the documentation so I figured I was missing something Solved by not hard locking on trying to make records work for no reason:
public abstract class ApiResponse<T>(T data)
{
[property: JsonPropertyName("data")]
public T Data { get; } = data;
}

public abstract class CharacterActionResult(Cooldown cooldown, Character character)
{
[JsonPropertyName("cooldown")]
public Cooldown Cooldown { get; } = cooldown;

[JsonPropertyName("character")]
public Character Character { get; } = character;
}

public class FightResult(Cooldown cooldown, Character character, Fight fight) : CharacterActionResult(cooldown, character)
{
[JsonPropertyName("fight")]
public Fight Fight { get; } = fight;
}
public abstract class ApiResponse<T>(T data)
{
[property: JsonPropertyName("data")]
public T Data { get; } = data;
}

public abstract class CharacterActionResult(Cooldown cooldown, Character character)
{
[JsonPropertyName("cooldown")]
public Cooldown Cooldown { get; } = cooldown;

[JsonPropertyName("character")]
public Character Character { get; } = character;
}

public class FightResult(Cooldown cooldown, Character character, Fight fight) : CharacterActionResult(cooldown, character)
{
[JsonPropertyName("fight")]
public Fight Fight { get; } = fight;
}
333fred
333fred22h ago
I know you said solved, but I don't think it's actually solved here. Let me ask a different way: write out, in full form, what you wanted Data to be in both of these locations
Lisa
LisaOP22h ago
The abstraction should include:
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
}
}
}
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
}
}
}
While the inheriting object should be able to add extras, such that the complete object contains:
// EXAMPLE 1
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
},
"fight": {
// Some big object
}
}
}

// EXAMPLE 2
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
},
"destination": {
// Some big object
}
}
}
// EXAMPLE 1
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
},
"fight": {
// Some big object
}
}
}

// EXAMPLE 2
{
"data": {
"cooldown": {
// Some big object
},
"character": {
// Some big object
},
"destination": {
// Some big object
}
}
}
333fred
333fred21h ago
So let's re-examine what you originally wrote. In your FightResult, you have a FightResultData, as opposed to a CharacterActionResultData in the base type Which of those is the type you actually want? What do you want to get when you have an instance of a FightResult?
Lisa
LisaOP21h ago
When I have a FightResult I actually want FightResultData. When I have a CharacterActionResult I actually want CharacterActionResultData
333fred
333fred21h ago
Ok, so the problem is that properties don't work this way Let me ask another question: why were you using records in the first place?

Did you find this page helpful?