Dynamic не містить визначення властивості з посилання на проект


93

Я отримую повідомлення про помилку:

'object' не містить визначення для 'Title'

весь код також на github

У мене є ConsoleApplication1, яка виглядає так

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Movie m = new Movie();
            var o = new { Title = "Ghostbusters", Rating = "PG" };
            Console.WriteLine(m.PrintMovie(o));
        }
    }
} 

та Movie.cs

public class Movie : DynamicObject
{
    public string PrintMovie(dynamic o)
    {
        return string.Format("Title={0} Rating={1}", o.Title, o.Rating);
    }
} 

він чудово працює з того самого проекту, але якщо я додаю ConsoleApplication2 з посиланням на ConsoleApplication1 і додаю Точний той самий код

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Movie m = new Movie();
            var o = new { Title = "Ghostbusters", Rating = "PG" };
            Console.WriteLine(m.PrintMovie(o));
        }
    }
}

Я отримую повідомлення про помилку:

'object' не містить визначення для 'Title' **

навіть якщо він знаходиться в динамічному об'єкті.

  • o.Title 'o.Title' видав виняток типу 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' динамічний {Microsoft.CSharp.RuntimeBinder.RuntimeBinderException}

Ось знімок екрану: введіть тут опис зображення

Я роблю щось подібне і намагаюся викликати функцію фільму з тестового проекту.


Відповіді:


79

Вам потрібно використовувати ExpandoObject

 dynamic o = new ExpandoObject();
 o.Title = "Ghostbusters";
 o.Rating = "PG";

 Console.WriteLine(m.PrintMovie(o));

28
Він зіткнувся з багатьма проблемами,
— написавши складне

2
Не здається, що ви можете використовувати функцію вбудованого ініціалізатора з об’єктом extendo?
— Роберто Боніні

1
Де слід використовувати ExpandoObject? для створення динамічного об'єкта або для синтаксичного аналізу динамічного об'єкта?
— Hosein Aqajani

Мені довелося шукати більше інформації, оскільки відповідь Роберта була корисною, але мені потрібно було глибше зрозуміти. Тут у Орейлі була хороша стаття про динамічні типи: oreilly.com/learning/building-c-objects-dynamically
— Біллі Віллоубі,

139

У відповіді Джахамала не сказано, чому ви отримуєте помилку. Причина в тому, що анонімний клас призначений internalдля збірки. Ключове dynamicслово не дозволяє вам обійти видимість учасників.

Рішення полягає в тому, щоб замінити анонімний клас на іменований публічний клас.

Ось ще один хороший приклад, що пояснює причину та інше можливе рішення .

Причиною data2.Personпомилки виклику є те, що інформація про тип data2недоступна під час виконання. Причина, по якій він недоступний, полягає в тому, що анонімні типи не є загальнодоступними. Коли метод повертає екземпляр цього анонімного типу, він повертає System.Object, яке посилається на екземпляр анонімного типу - типу, інформація якого недоступна для основної програми. Динамічний час виконання намагається знайти властивість, що викликається Personдля об'єкта, але не може вирішити це з інформації про тип, яку він має. Як такий, він створює виняток. Заклик до data.Nameвідмінно працює, оскільки Personє загальнодоступним класом, ця інформація доступна і може бути легко вирішена.

Це може вплинути на вас у будь-якому з наступних випадків (якщо не більше):

  1. Ви повертаєте непублічний, не внутрішній тип використання System.Object. 2. Ви повертаєте непублічний, не внутрішній похідний тип через загальнодоступний базовий тип і отримуєте доступ до властивості у похідному типі, яке не є базовим типом. 3. Ви повертаєте будь-що, загорнуте в анонімний тип, з іншої збірки.

1
Чи не могли б Ви навести своє джерело у своїй відповіді, будь ласка?
— d3dave

@ d3dave Два твердження у відповіді можна перевірити. Видимість класу можна перевірити в декомпіляторі .NET. Правила доступу для dynamicможна перевірити на тестовому класі з учасниками різної видимості.
— Роберт Важан,

3
Це справжня відповідь на те, чому те, що робив ОП, є проблемою.
— Матті Вірккунен

1
Я не можу змусити це працювати між джерелом та тестовим проектом, які одночасно є netcoreapp1.1. Будь-яка ідея, чи це лише моя вина, чи це не працює в .NET Core?
— Ентоні Мастреан

29

У моєму випадку у мене був проект Unit Test, який я створив у Visual Studio, і багато випадків, коли мені потрібно було протестувати методи в бібліотеці рівня даних. Я не хотів змінювати їх усіх, тому позначив тестову збірку як друга, використовуючи:

[assembly:InternalsVisibleTo("MyDataLayerAssemblyName")]

І це вирішило.

Приклад:

using System.Runtime.CompilerServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: InternalsVisibleTo( "MyDataLayerAssembly" )]
namespace MyUnitTestProject.DataTests
{

   [TestClass]
   public class ContactTests
   {
      ...

Список літератури:


1
Причина в тому, що сказав Олександр Степанюк. Ваш коментар - це рішення. Дякую!
— Пато Локо

Я не можу змусити це працювати між проектами netcoreapp1.1, не впевнений, що це щось я роблю неправильно.
— Ентоні Мастреан,

Величезне спасибі Єлгаба! Тепер мені не потрібно замінювати динамічний на ExpanoObject! Я використовую ін’єкцію залежностей у своїх модульних тестах, і я не зміг використовувати динамічну систему, щоб вона працювала за проектом модульної перевірки. Але це вирішило!
— ShameWare

Зверніть увагу, що ви (розробник) повинні додати це у протилежному проекті, з якого створюються анонімні типи, або в обох випадках, якщо це так.
— ryanwebjackson

0

У моєму випадку у мене є проект тесту xUnit.

Де 'вміст' - це рядок json .

Цей код видає помилку:

dynamic parsed = JsonConvert.DeserializeObject<dynamic>(content);

Цей код працює. Використовуйте ExpandoObject, вкладений у динаміку, як це:

dynamic parsed = JsonConvert.DeserializeObject<ExpandoObject>(content);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.