Передача значения в метод с использованием пользовательского атрибута - PullRequest
1 голос
/ 11 марта 2019

Я пытаюсь понять, как я могу использовать пользовательские атрибуты для вызова метода, передающего параметры

    [ExecuteMe("hello", "reflection")]
    public void M3(string s1, string s2)
    {
        Console.WriteLine("M3 s1={0} s2={1}", s1, s2);
    }

Я пытаюсь вызвать этот метод, используя этот код:

static void Main(string[] args)
    { 
        var assembly= Assembly.LoadFrom("MyLibrary.dll");

        foreach (var type in assembly.GetTypes())
        {
            object act = Activator.CreateInstance(type);

            var  methodInfos = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(ExecuteMe)).Any());
            foreach (var mInfo in methodInfos)
            {
                //Console.WriteLine(mInfo.Name);
                var argument =  mInfo.GetParameters();

                foreach (var a in argument)
                {

                    Console.WriteLine(a);
                   // a.RawDefaultValue;
                   mInfo.Invoke(act, new object[]{a});
                }


            }


            if (type.IsClass)
                Console.WriteLine(type.FullName);

        }
        Console.ReadLine();

    }

Это не работает, потому что «a» - это ParameterInfo и вызывать want a Object [].Что я делаю не так и как мне получить эти значения?

это мой атрибут:

public class ExecuteMe : Attribute
{
    public object[] args;

    public ExecuteMe(params object[] _args)
    {

            this.args = _args;

    }
}`

Ответы [ 2 ]

0 голосов
/ 11 марта 2019

Я его немного переписал. На самом деле вы никогда не обращались к аргументам, указанным для вашего атрибута.

namespace StackOverflow
{
    using System;
    using System.Reflection;

    [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
    public class ExecuteMe : Attribute
    {
        public object[] Arguments { get; }

        public ExecuteMe(params object[] args)
        {
            this.Arguments = args;
        }
    }

    public class TestSubject
    {
        [ExecuteMe(5, "Hello")]
        [ExecuteMe(7, "World")]
        public int Function(int i, string s)
        {
            Console.WriteLine("Executing TestSubject.Function with parameters {0} and {1}", i, s);

            return 42;
        }
    }

    internal static class Program
    {
        internal static void Main(string[] args)
        {
            // This could come from another dll, for example
            // var assembly = Assembly.LoadFrom("MyLibrary.dll").GetTypes();
            var availableTypes = Assembly.GetExecutingAssembly().ExportedTypes;

            foreach (var type in availableTypes)
            {
                foreach (var method in type.GetMethods())
                {
                    foreach (var attribute in method.GetCustomAttributes<ExecuteMe>())
                    {
                        var instance = Activator.CreateInstance(type);

                        method.Invoke(instance, attribute.Arguments);
                    }
                }
            }

            Console.ReadLine();
        }
    }
}

Это должно дать:

enter image description here

0 голосов
/ 11 марта 2019

Чтобы убедиться, что я понимаю, что вы пытаетесь сделать, если метод имеет атрибут ExecuteMe, вы хотите вызвать метод, передав аргументы из атрибута в метод?

IЯ собираюсь предположить, что это только для экспериментов, и вы уже понимаете, что это не гарантирует, будет ли количество или тип аргументов, предоставленных для атрибута, соответствовать количеству и типу аргументов, которые требует метод.Атрибут принимает неограниченное количество объектов, а метод требует две строки.

Проблема, с которой вы сталкиваетесь, заключается в том, что вы смотрите на .GetParameters, который ничего не говорит вам о приходящих значениях.из атрибута.Он просто описывает параметры метода.

Вам нужно получить свойство args из атрибута и передать эти значения при вызове метода.

Только дляВ качестве иллюстрации я собираюсь использовать метод, в котором сигнатуры совпадают.

public class ClassWithMethod
{
    [ExecuteMe("hello", "reflection")]
    public void M3(params object[] args)
    {
        var strings = args.Where(arg => arg != null).Select(arg => arg.ToString());
        Console.WriteLine(string.Join(", ", strings));
    }

    // Just to verify that we're only invoking methods with the attribute.
    public void MethodWithoutAttribute() { }
}

... и в консольном приложении я просто для удобства собираюсь читать типы из исполняемой сборки.

Я переставил несколько вещей, но вы увидите, что происходитon:

static void Main(string[] args)
{
    var assembly = Assembly.GetExecutingAssembly();

    foreach (var type in assembly.GetTypes())
    {
        var methodInfos = type.GetMethods();

        // looking at all the methods, not yet narrowing it down to those
        // with the attribute.
        foreach (var mInfo in methodInfos)
        {
            // We don't just want to know if it has the attribute.
            // We need to get the attribute.
            var executeMeParameter = mInfo.GetCustomAttribute<ExecuteMe>();

            // If it's null the method doesn't have the attribute. 
            // Ignore this method.
            if (executeMeParameter == null) continue;

            // We don't need to create the instance until we know that we're going
            // to invoke the method.
            object act = Activator.CreateInstance(type);

            // Pass the args property of the attribute (an array of objects)
            // as the argument list for the method.
            mInfo.Invoke(act, new object[]{executeMeParameter.args});
        }

        if (type.IsClass)
            Console.WriteLine(type.FullName);
    }
    Console.ReadLine();
}

В этом случае мы просто передаем все аргументы из атрибута.Это та часть, где немного грязно.Что если args имеет три строковых значения, а метод имеет один параметр int?

Эта часть немного странная.Я должен был сделать это из-за ключевого слова params.

mInfo.Invoke(act, new object[]{executeMeParameter.args});
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

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

[ExecuteMe]
public void CallM3()
{
    M3("Hello", "reflection");
}

public void M3(params object[] args)
{
    var strings = args.Where(arg => arg != null).Select(arg => arg.ToString());
    Console.WriteLine(string.Join(", ", strings));
}

... и у атрибута нет аргументов:

public class ExecuteMe : Attribute
{
}

Разница в том, что все строго типизировано и компилируется.Вам не нужно беспокоиться о том, будут ли параметры совпадать во время выполнения.

...