I'm using C# with the .NET 6 framework. I have a class called
Message
and another called TaggedMessage
which inherits from Message
.
The idea is simple. A function receives an object of type Message and then adds several Tags to it and returns it as a TaggedMessage. A list of TaggedMessage objects is later displayed in a table. For databinding to remain nice and easy I want TaggedMessage to not contain nested properties. So it shouldn't hold an instance of Message for example. Instead it should contain all the properties from Message plus additional ones.
So I thought it should inherit from Message. However I cannot find a way to instantiate TaggedMessage from Message unless I specifically assign every column from Message to TaggedMessage in its constructor. Which seems overly difficult and would mean everytime I add a property to Message, I would have to revisit the constructor of TaggedMessage. Exmaple (obviously the real thing is more complex)
public class Message
{
public string MessageID { get; set; } = "5";
public string Subject{ get; set; } = "Test";
}
Public class TaggedMessage : Message
{
public string MyTag { get; set; }
}
Message m = new Message();
TaggedMessage t = TaggedMessage;
t = (TaggedMessage)m; //This ovbiously doesn't work
t.Tag = "Nature";
Now the casting doesn't work because I'm casting a base class in a derived class. But then, how to I get the values from m into t? Let's assume m has 50 properties and they could change in the future. How can get an object t that has all the values m had, but with extra tags added? There must be a more elegant way than assigning all 50 properties in the constructor!? I feel like I'm missing a simple solution here.
CodePudding user response:
Message
object cannot be cast to a TaggedMessage
type.
What are you looking for is called mapping and there are a lot of libraries for that, including but not limited to Automapper, Mapster or ExpressMapper for example.
AutoMapper:
static void Main()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Message, TaggedMessage>()
.IncludeAllDerived();
});
var mapper = config.CreateMapper();
var m = new Message() { MessageID = "SomeMessageID", Subject = "SomeSubject" };
var t = mapper.Map<TaggedMessage>(m);
t.MyTag = "MyTag";
Console.WriteLine(t.MessageID);
Console.WriteLine(t.Subject);
Console.WriteLine(t.MyTag);
}
CodePudding user response:
There are certain ways to do what you intend to do without bothering writing the mappings manually. One of them is using a library that does it for you, like AutoMapper as Viachaslau suggests.
Another can can be serializing and deserializing the object:
var message = new Message();
var str = System.Text.Json.JsonSerializer.Serialize(message);
var taggedMessage = System.Text.Json.JsonSerializer.Deserialize<TaggedMessage>(str);
taggedMessage.MyTag = "Nature";
Both alternatives to writing that code in the constructor have their cons and can be expensive in their own ways depending on the use case, so it's not about whether you can avoid it, but about whether you should do it or not!