【问题标题】:Passsing a value to a method using a custom attribute使用自定义属性将值传递给方法
【发布时间】:2019-03-11 15:02:13
【问题描述】:

我正在尝试了解如何使用自定义属性来调用传递参数的方法

    [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 并且调用需要一个 Object[]。 我做错了什么以及如何获得这些值?

这是我的属性:

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

    public ExecuteMe(params object[] _args)
    {

            this.args = _args;

    }
}`

【问题讨论】:

  • 您永远不会真正访问您找到的属性的args 成员。也许您应该首先将其打印到控制台。
  • 抱歉,您能说得具体一点吗?

标签: c# reflection invoke custom-attributes


【解决方案1】:

我已经重写了一点。您从未真正访问过您为属性提供的参数。

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();
        }
    }
}

这应该产生:

【讨论】:

    【解决方案2】:

    为了确保我理解您要执行的操作,如果方法具有 ExecuteMe 属性,您想调用该方法,将参数从属性传递给方法吗?

    我将假设这只是为了进行实验,并且您已经意识到这并不能保证提供给属性的参数的数量或类型是否与方法所需的参数的数量和类型相匹配。属性接受无限数量的对象,而方法需要两个字符串。

    您看到的问题是您正在查看.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() { }
    }
    

    ...在控制台应用程序中,我将从正在执行的程序集中读取类型,只是为了方便。

    我重新安排了一些事情,但你会看到发生了什么:

    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
    {
    }
    

    现在的不同之处在于,所有内容都是强类型并可以编译的。您不必担心参数是否会在运行时匹配。

    【讨论】:

      猜你喜欢
      • 2015-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-14
      相关资源
      最近更新 更多