使用自定义属性将值传递给方法

时间:2019-03-11 15:02:13

标签: c# reflection invoke custom-attributes

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

    [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;

    }
}`

1 个答案:

答案 0 :(得分:0)

为确保我理解您要执行的操作,如果某个方法具有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
{
}

现在的区别是所有内容都经过强类型化和编译。您不必担心参数在运行时是否匹配。