Класс со свойством, которое может быть одного из двух разных типов

Я разрабатываю бота Telegram на С#, но у меня проблемы с реализацией типа Message. Согласно документации по API, поле chat может иметь тип User или тип GroupChat. Как реализовать это на С#?

До сих пор я мог только придумать следующий код, используя Newtonsoft.Json:

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

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

Но это не работает с моим методом контроллера WebAPI 2, так как я десериализую Message с помощью атрибута FromBody:

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

(тип Update имеет поле message типа Message)

Есть ли лучший способ реализовать тип Message?


person Azimuth    schedule 05.08.2015    source источник
comment
Лучше сказать, что свойство класса может быть одним из двух разных типов.   -  person crashmstr    schedule 05.08.2015
comment
@crashmstr правильно, я переименовал вопрос   -  person Azimuth    schedule 06.08.2015


Ответы (3)


Поскольку вы должны обрабатывать, казалось бы, два совершенно разных объекта в одном поле, я не думаю, что вы можете использовать строго типизированные объекты, но вы можете использовать динамический тип для поля чата, такого как

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

Затем вы можете получить доступ к переменным и проверить, являются ли они нулевыми или нет, чтобы понять, является ли это пользователем или групповым чатом:

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
}
person Volkan Paksoy    schedule 05.08.2015

Я столкнулся с этой проблемой и в Java. Поскольку и C#, и Java являются строго типизированными языками, моя проблема была похожей. Я решил это, создав общий класс Chat. Этот класс Chat содержит свойства из User, а также из GroupChat. В классе также есть методы isUser(), isGroupChat(), asUser() и asGroupChat().

Я должен признать, что не знаю C#, поэтому не могу написать для вас пример на этом языке, но вот тело класса Chat на Java:

@SerializedName("id")
private int id;

@SerializedName("first_name")
private String firstName;

@SerializedName("last_name")
private String lastName;

@SerializedName("username")
private String username;

@SerializedName("title")
private String title;

public boolean isUser() {
    return title == null;
}

public boolean isGroupChat() {
    return !isUser();
}

public User asUser() {
    return new User(id, firstName, username, lastName);
}

public GroupChat asGroupChat() {
    return new GroupChat(id, title);
}

Это довольно многословно и добавляет дублирующийся код, поэтому, если у кого-то есть какие-либо дополнения, я также хотел бы их услышать.

person Pete    schedule 06.08.2015

Вы можете построить свой тип Message как интерфейс (IMessage), а затем реализовать его с помощью классов GroupMessage и UserMessage.

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;} }
}

Но что я действительно сделал бы, так это справился бы с этим на более высоком уровне. Глядя на документы здесь, а не на сложный объект, который может обрабатывать все это, я бы подумал с точки зрения отправки или получения сообщения. Затем я думал обо всех действиях, которые вы, возможно, захотите выполнить при получении сообщения. Оттуда у меня будет объект, который может вызвать событие для каждого из этих действий. Когда вы получаете данные (не думайте об этом как о сообщении), вы анализируете данные и, возможно, создаете несколько событий из того, что было одним объектом сообщения Telegram. Для некоторых из этих событий могут потребоваться определения классов для данных, необходимых для события, но сейчас мы говорим о чем-то гораздо более узком; лучше подходит для принципа единой ответственности.

Идя в другом направлении (отправляя данные, а не получая), у объекта будут методы для различных вещей, которые вы можете делать при отправке данных. Некоторым из этих методов могут потребоваться классы для необходимых данных. По возможности используйте те же классы, что и для получения событий. Это может быть даже один метод Send() со многими перегрузками.

person Joel Coehoorn    schedule 07.08.2015