Могу ли я заставить dynamic работать так?

#c# #dynamic #duck-typing

#c# #динамический #утиный ввод

Вопрос:

У меня занятия с Пациентами:

 class Patient {
  public string First_Name { get; set; }
  public string Last_Name { get; set; }
  public DateTime Date_of_Birth { get; set; }
}
  

У меня также есть интерфейс:

 interface IPerson {
  string First_Name { get; }
  string Last_Name { get; }
}
  

в этом консольном приложении я бы хотел, чтобы метод Display_Person работал. Он компилируется, но выдает ошибку во время выполнения, потому что Patient не реализует IPerson.

 class Program {
  static void Main(string[] args) {
    Patient p = new Patient { 
      First_Name = "Charles", Last_Name = "Lambert",
      Date_of_Birth = new DateTime(1976,5,12),
    };
    Display_Person(p);
  }

  static void Display_Person(dynamic person) {
    IPerson p = person;
    Console.WriteLine("{0}, {1}", p.Last_Name, p.First_Name);
  }
}
  

Какие изменения в коде, не требуя от пациента реализации интерфейса IPerson, я могу внести, чтобы заставить метод Display_Person работать? Я бы предпочел решение, которое можно использовать повторно.

Обновление: я хочу, чтобы это сработало, чтобы я мог получить intellisense. Пожалуйста, не обращайте внимания на тривиальность этого примера. Это коротко и по существу объясняет мою проблему. Если бы это было 1003 заявки на получение кредита (при печати размером с книгу), я бы не хотел применять более 20 интерфейсов к своему классу, чтобы я мог группировать связанные данные для вычислений. Мне также не хотелось бы каждый раз вводить все эти свойства. Отсутствие intellisense в прошлом отвлекало меня от использования динамических языков. (Я не ленивый, я эффективный!)

Комментарии:

1. Если вы действительно хотите, чтобы intellisense работал, используйте его, как вы делали в своем примере, избавьтесь от строки IPerson и измените параметр ‘person’ на ‘p’. Не идеально, но intellisense знает столько, сколько позволяет ваш код.

Ответ №1:

Для этого вы можете использовать импровизированный интерфейс.

Комментарии:

1. как бы ни было круто ваше предложение. Это не работает с непубличными интерфейсами. Также, если динамический тип на самом деле является определенным типом, например, моим Patient классом. Он выдает ошибку времени выполнения, которая Patient не реализует ActLike() метод.

2. Это странно, я широко использовал его для приведения динамических объектов к интерфейсам. Вы пробовали альтернативный синтаксис: Impromptu.ActLike<IMyInterface>(expando); ?

3. Он отлично работает, если он общедоступный. dynamicVariableName.ActLike<IInterface>() вообще не сработало. `Экспромт. ActLike<IInterface>() работает только тогда, когда интерфейс является общедоступным. Я думаю, это не может отразиться на внутренних членах.

4. Я попытался добавить an InternalsVisibleToAttribute в сборку, и это все равно не решило проблему. Я предполагаю, что импровизированная библиотека отражает только общедоступные элементы. Я собираюсь отправить туда свой пример кода и посмотреть, не ошибка ли это.

5. @CharlesLambert Проблема не в том, чтобы размышлять о внутренних элементах, это работает нормально, сложность заключается в создании нового типа, который наследуется от внутреннего интерфейса. [assembly: InternalsVisibleTo("ImpromptuInterfaceDynamicAssembly")] тем не менее, следует решить проблему.

Ответ №2:

Если вы действительно хотите использовать dynamic, тогда зачем преобразование?

 static void Display_Person(dynamic person) {
    Console.WriteLine("{0}, {1}", person.Last_Name, person.First_Name);
}
  

Но я бы все же предпочел, чтобы Patient реализовал IPerson. Это имело бы гораздо больше смысла с точки зрения дизайна, а безопасность типов означает, что любая ошибка будет обнаружена во время компиляции, а не во время выполнения.

Ответ №3:

Нет! Я не думаю (без реализации IPatient), что это возможно. Вы должны реализовать IPatient интерфейс

 class Patient : IPatient {}
  

Или удалить приведение.

 static void Display_Person(dynamic person) 
{
    Console.WriteLine("{0}, {1}", person.Last_Name, person.First_Name);
}
  

Ответ №4:

IPerson не нужен. Просто сделайте Display_Person так:

 static void Display_Person(dynamic person) {
    Console.WriteLine("{0}, {1}", person.Last_Name, person.First_Name);
}
  

Пока person эти свойства реализуются, это работает нормально.