Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class with property that can be one of two different types

I'm developing a Telegram bot in C# but have difficulty with implementing Message type. According to API documentation, chat field can be either of type User or of type GroupChat. How do I implement that in C#?

So far I could only come up with following code using Newtonsoft.Json:

public class Update {
....
  [JsonProperty("chat")]
  public User chat { get; set; }

  [JsonProperty("chat")]
  public GroupChat group_chat { get; set; }
....
}

But it doesn't work with my WebAPI 2 controller method since I deserialize Message using FromBody attribute:

public async Task<HttpResponseMessage> Post(string token, [FromBody] Update update)

(type Update has a field message of type Message)

Is there any better way to implement Message type?

like image 497
Azimuth Avatar asked Aug 05 '15 09:08

Azimuth


People also ask

What is a property of a class c#?

A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field. Properties can be used as if they're public data members, but they're special methods called accessors.

Why do we use get and set properties in c#?

It is a good practice to use the same name for both the property and the private field, but with an uppercase first letter. The get method returns the value of the variable name . The set method assigns a value to the name variable. The value keyword represents the value we assign to the property.

Can c# properties be private?

Properties can be marked as public , private , protected , internal , protected internal , or private protected . These access modifiers define how users of the class can access the property.


2 Answers

Since you are supposed to handle seemingly two completely different objects in one field I don't think you can use strongly-typed objects but you can use a dynamic type for the chat field such as

public class Telegram
{
    public int message_id { get; set; }
    public dynamic chat { get; set; }
}

Then you can access the variables and check if they are null or not to understand if it's a user or a group chat:

static void Main(string[] args)
{
    string json = @"{ ""chat"": { ""id"": 1, ""first_name"": ""my_first_name"", ""last_name"": ""my_last_name"",  ""username"": ""my_username"" }, ""message_id"": 123 }";
    var telegram = Newtonsoft.Json.JsonConvert.DeserializeObject<Telegram>(json);
    string name = telegram.chat.first_name; // name = my_first_name
    string title = telegram.chat.title; // title = null
}
like image 87
Volkan Paksoy Avatar answered Oct 05 '22 21:10

Volkan Paksoy


You could build your Message type as an interface (IMessage), and then implement it with GroupMessage and UserMessage classes.

interface IMessage
{
   int id;

   //...

   object chat {get;}
}

public class GroupMessage : IMessage
{
   public int id;
   public GroupChat group;
   public object chat {get {return group;} }
}

public class UserMessage : IMessage
{
   public int id;
   public User user;
   public object chat {get {return user;} }
}

But what I would really do is handle this at a higher level. Looking at the docs here, rather than a complex object that can handle all of this I would think in terms of sending or receiving a message. Then I would think of all of the actions you might want to do when receiving a message. From there I would have an object that can raise an event for each of those actions. When you receive data (don't think of it as a message), you parse the data and perhaps raise several events from what was one Telegram message object. Some of those events may require class definitions for the data needed by the event, but now we're talking something that's much narrower in scope; a better fit for the Single Responsibility Principle.

Going the other direction (sending data rather than receiving), the object would have methods for the different kinds of things you might do when sending data. Some of those methods may require classes for the data needed. When possible, use the same classes you did for the receiving events. It might even be just one Send() method with many overloads.

like image 35
Joel Coehoorn Avatar answered Oct 05 '22 22:10

Joel Coehoorn