dynamic не содержит определения свойства из ссылки на проект

93

Я получаю сообщение об ошибке:

'объект' не содержит определения для 'Заголовка'

весь код тоже на 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));
        }
    }
}

Я получаю сообщение об ошибке:

'объект' не содержит определения для 'Заголовка' **

даже если он находится в динамическом объекте.

  • o.Title 'o.Title' вызвало исключение типа 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' dynamic {Microsoft.CSharp.RuntimeBinder.RuntimeBinderException}

Вот скриншот: введите описание изображения здесь

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

eiu165
источник

Ответы:

79

Вам нужно использовать ExpandoObject

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

 Console.WriteLine(m.PrintMovie(o));
Джамахал SOF
источник
28
У него было много проблем, чтобы написать сложный вопрос, было бы неплохо сообщить ему, почему он получает ошибку, как предлагает Роберт
Луис Феррао
2
Не кажется, что вы можете использовать функциональность встроенного инициализатора с объектом expando?
Роберто Бонини
1
Где использовать ExpandoObject? для создания динамического объекта или для анализа динамического объекта?
Хосейн Акаджани
Мне пришлось искать дополнительную информацию, поскольку ответ Роберта был полезен, но мне нужно было более глубокое понимание. У Орейли была хорошая статья о динамических типах здесь: oreilly.com/learning/building-c-objects-dynamically
Билли Уиллоби
139

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

Решение состоит в том, чтобы заменить анонимный класс именованным публичным классом.

Вот еще один хороший пример, объясняющий причину и другое возможное решение .

Причина data2.Personсбоя вызова в том, что информация о типе data2недоступна во время выполнения. Причина, по которой он недоступен, заключается в том, что анонимные типы не являются общедоступными. Когда метод возвращает экземпляр этого анонимного типа, он возвращает System.Object, который ссылается на экземпляр анонимного типа - типа, информация о котором недоступна для основной программы. Среда динамического выполнения пытается найти свойство, вызываемое Personу объекта, но не может разрешить его на основе имеющейся у него информации о типе. Таким образом, он вызывает исключение. Вызов to data.Nameработает нормально, поскольку Personэто открытый класс, эта информация доступна и может быть легко решена.

Это может повлиять на вас в любом из следующих случаев (если не больше):

  1. Вы возвращаете закрытый, не внутренний тип, используя System.Object. 2. Вы возвращаете закрытый, не внутренний производный тип через общедоступный базовый тип и получаете доступ к свойству в производном типе, которого нет в базовом типе. 3. Вы возвращаете все, что заключено в анонимный тип, из другой сборки.
Роберт Важан
источник
1
Не могли бы вы указать свой источник в своем ответе?
d3dave
@ d3dave Два утверждения в ответе можно проверить. Видимость класса можно проверить в декомпиляторе .NET. Правила доступа для dynamicмогут быть проверены на тестовом классе с членами различной видимости.
Роберт Важан
3
Это настоящий ответ на вопрос, почему то, что делал OP, является проблемой.
Матти Вирккунен
1
Я не могу заставить это работать между исходным и тестовым проектами, которые оба являются netcoreapp1.1. Есть идеи, это только моя вина или это не работает в .NET Core?
Энтони Мастрян
29

В моем случае у меня был проект модульного теста, который я создал в 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
Причина в том, что сказал Александр Степанюк. Ваш комментарий - это решение. Благодарность!
Pato Loco
Я не могу заставить это работать между проектами netcoreapp1.1, не уверен, что я что-то делаю неправильно.
Энтони Мастрян
Огромное спасибо, Джелгаб! Теперь мне не нужно заменять динамический на ExpanoObject! Я использую внедрение зависимостей в своих модульных тестах, и мне не удалось использовать динамический и заставить его работать из проекта модульного теста. Но это решило ее!
ShameWare
Обратите внимание, что вы (разработчик) должны добавить это в противоположный проект, из которого создаются анонимные типы, или в оба, если это так.
ryanwebjackson
0

В моем случае у меня есть тестовый проект xUnit.

Где «контент» - это строка в формате json .

Этот код выдает ошибку:

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

Этот код работает. Используйте ExpandoObject вместо dynamic следующим образом:

dynamic parsed = JsonConvert.DeserializeObject<ExpandoObject>(content);
Гильерме Феррейра
источник