Передача анонимного типа в качестве параметров метода

В моей архитектуре плагина я в настоящее время передаю имя плагина (строку), имя метода (строку) и параметры (массив объектов) в службу моего плагина, чтобы выполнить указанный метод и вернуть результат (типа T).

Метод выполнения службы плагина можно увидеть ниже:

public TResult Execute<TResult>(string pluginName, string operation, params object[] input) {
    MethodInfo method = null;
    TResult result = default(TResult);

    var plugin = _plugins.Enabled().FirstOrDefault(x => x.GetType().Name.Equals(pluginName,  StringComparison.InvariantCultureIgnoreCase));

    if (plugin != null) {
        method = plugin.GetType().GetMethods().FirstOrDefault(x => x.Name == operation);
        if (method != null) {
            result = (TResult)method.Invoke(plugin, input);
        }
    }
    return result;
  }

Пример использования:

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin",
    "GetImageUrl",
    new object[] { image, size });

Я бы предпочел вместо этого передать анонимный тип (поскольку я думаю, что это более читабельно), т.е.

var url = AppHelper.PluginService.Execute<string>(
    "ImagePlugin",
    "GetImageUrl",
    new { image = image, targetSize = size });

Как мне изменить свой метод Execute, чтобы сопоставить свойства анонимного типа с параметрами метода моего плагина?

Я рассматривал возможность использования нового динамического типа в .net 4.0, но я предпочитаю определять свои параметры в методе плагина, а не принимать один динамический объект.

Спасибо Бен

[Обновлять]

После просмотра исходного кода ASP.NET MVC кажется достаточно простым вытащить анонимный тип в словарь объектов, например. RouteValueDictionary. С помощью отражения выражение linq создается динамически. Несмотря на то, что это хорошая реализация, мне на самом деле не нужна была вся эта дополнительная сложность.

Согласно приведенному ниже комментарию, я могу добиться удобочитаемости, просто указав свои параметры в строке (нет необходимости в объявлении массива объектов):

var url = AppHelper.PluginService.Execute<string>("ImagePlugin", "GetImageUrl", image, size);

person Ben Foster    schedule 08.08.2010    source источник
comment
Поскольку вы используете ключевое слово params, вы можете использовать image, size вместо new object[] { image, size }. Это сделало бы его более читабельным, а поскольку Invoke методы принимают массив объектов, я бы оставил подпись метода как есть.   -  person Necros    schedule 09.08.2010


Ответы (9)


Есть несколько способов сделать это возможным, хотя я бы не советовал никому из них.

Во-первых, вы можете использовать отражение, что означает, что вам нужно написать много дополнительного (подверженного ошибкам) ​​кода в вашем PluginService.Execute методе, чтобы получить желаемые значения.

Во-вторых, если вам известны параметры анонимного типа, которые вы передаете своему методу, вы можете использовать описанную технику здесь. Вы можете привести к другому анонимному типу внутри вашего метода, который имеет те же свойства. Здесь это еще одно описание той же техники от Джона Скита.

В-третьих, вы можете использовать классы из System.ComponentModel. Например, это используется в ASP.NET MVC. Он использует отражение под капотом. Однако в ASP.NET MVC либо имена свойств хорошо известны (например, controller и action), либо их имена не имеют значения, потому что они передаются как есть в метод контроллера (например, id).

person Ronald Wildenberg    schedule 08.08.2010
comment
Рональд, спасибо за ссылки. Начинаю думать, что мне следует придерживаться моей текущей реализации. Однако разве это не то, что широко используется в ASP.NET MVC, например, в помощниках HTML и при маршрутизации. Я уверен, что должен быть хороший способ сделать это, иначе Microsoft бы этого не сделала :) - person Ben Foster; 08.08.2010
comment
Хорошая точка зрения. Я обновил свой ответ другим способом сделать то, что вы хотите. - person Ronald Wildenberg; 08.08.2010
comment
Я отметил ваш ответ как наиболее полный. Однако, просмотрев исходный код MVC, чтобы увидеть, как они этого достигают, я собираюсь придерживаться своей реализации, но использовать предложение Necros для улучшения читаемости. - person Ben Foster; 09.08.2010
comment
Ваша ссылка на Джона Скита мертва - person Detail; 06.08.2018
comment
Спасибо, исправил ссылку - person Ronald Wildenberg; 07.08.2018

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

Однако я бы также добавил, что после изучения новых динамических функций в .net 4.0, таких как ExpandoObject, стало намного удобнее передавать динамический объект в качестве параметра:

        dynamic myobj = new ExpandoObject();
        myobj.FirstName = "John";
        myobj.LastName = "Smith";

        SayHello(myobj);
        ...........

        public static void SayHello(dynamic properties)
        {
           Console.WriteLine(properties.FirstName + " " + properties.LastName);
        }
person Ben Foster    schedule 06.09.2010

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

var o = new { FirstName = "John", LastName = "Doe" };

var result = MyMethod(o);

string MyMethod(dynamic o)
{
    return o.FirstName + " " + o.LastName;
}

Подробнее читайте в этом сообщении блога

person Alex T.    schedule 06.11.2011

В этом примере анонимный объект преобразуется в словарь:

IDictionary<string, object> AnonymousObjectToDictionary(object propertyBag)
{
    var result = new Dictionary<string, object>();
    if (propertyBag != null)
    {
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(propertyBag))
        {
            result.Add(property.Name, property.GetValue(propertyBag));
        }
    }
    return result;
}

Вы можете назвать это так:

AnonymousObjectToDictionary(new { foo = 11, bar = "Be happy" });
person Pavel Chuchuva    schedule 13.08.2012

Если это автономный тип из Linq, вы можете легко сделать это, передав IEnumerable.

Вот пример метода получения

    public static void MyMethod<IEnumType>(ref IEnumerable<IEnumType> ienum)
    {
        using (DataTable dt = new DataTable())
        {
            ienum.First(ie => true).GetType().GetProperties().ToList().ForEach(pr => dt.Columns.Add(pr.Name, typeof(string))); //Parallelization not possible since DataTable columns must be in certain order

            ienum.ToList().ForEach(ie => //Parallelization not possible since DataTable rows not synchronized.
                {
                    List<object> objAdd = new List<object>();
                    ie.GetType().GetProperties().ToList().ForEach(pr => objAdd.Add(ie.GetType().InvokeMember(pr.Name, BindingFlags.GetProperty, null, ie, null))); //Parallelization not possible since DataTable columns must be in certain order
                    dt.Rows.Add(objAdd.ToArray());
                    objAdd.Clear();
                    objAdd = null;
                });
            //Do something fun with dt
        }
    }

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

person William    schedule 24.09.2012

Привет, я написал об этом сообщение:

http://blog.jorgef.net/2011/06/converting-any-object-to-dynamic.html

Надеюсь, это поможет.

person Jorge Fioranelli    schedule 23.06.2011

Я сделал это однажды. Что вы можете сделать, так это получить параметры, ожидаемые от функции, через отражение. Затем вы можете создать свой массив параметров, сопоставив имена в массиве параметров с ключами анонимного объекта.

Надеюсь, это поможет :-).

person Alxandr    schedule 08.08.2010

Прежде всего, проверьте пространство имен System.Addin, там вам может помочь.

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

person Akash Kava    schedule 08.08.2010
comment
Мы уже используем интерфейсы вместе со StructureMap для вызова определенных типов плагинов. Это больше для подключаемых модулей, которые пользователь может создавать на лету и хочет использовать в пользовательском интерфейсе (например, они могут захотеть заменить ImagePlugin, указанный выше, на тот, который извлекает изображения из Amazon S3). - person Ben Foster; 08.08.2010

В C # 7.0 вы можете использовать кортежи. https://docs.microsoft.com/en-us/dotnet/csharp/tuples

Вот пример:

TestMetchod(("String 1", 342));

private void TestMetchod((string Param1, int Param2) p)
{
    //do something
}

Надеюсь, это поможет.

person SzymonB    schedule 21.03.2020