【问题标题】:How to call methods with method attributes?如何调用具有方法属性的方法?
【发布时间】:2018-03-03 17:11:38
【问题描述】:

我在类中有多个方法实例,我需要快速调用和编写这些方法,而无需将它们添加到我的主函数中。这将如何用一个属性来完成?

例如 我有很多不同的类都有一个叫做“调用”的方法。我想添加一个自定义属性,我可以将其添加到此方法中,然后在称为“全部调用”的不同方法中对这些类中的每一个调用调用方法。

类似的东西看起来像这样,但很实用。

public class main_class
{ 
   public void invoke_all()
   {
      // call all the invokes
   }

}

public class test1
{
   [invoke]
   public void invoke()
   {
      Console.WriteLine("test1 invoked");
   }
}
public class test2
{
   [invoke]
   public void invoke()
   { 
     Console.WriteLine("test2 invoked");
   }
}

【问题讨论】:

  • 你能再解释一下,为什么你需要那个?
  • 如何调用非静态方法?先构造一个test1对象?
  • 我宁愿建议在main_class 中有一个delegate,它可以有一个单例实例。将要调用的所有方法一起添加到每个类中的委托。调用此委托将依次调用所有方法。
  • 这是一个例子,我不知道怎么做。这只是我的话。
  • @praty 我可以得到一个代码示例吗?

标签: c# .net


【解决方案1】:

这对我来说有点不清楚,但我想我有一个解决方案:

您不能直接按照您的要求进行操作,但有一种解决方法 - 创建仅包含一种方法的接口:invoke(或更多,请随意),在 main 方法中,创建实现您的接口的对象列表 -或者只是创建为一个字段并在该方法中使用它。然后,在简单的foreach 循环中,您可以对列表中的每个项目调用Invoke 方法(这是可能的,因为它们实现了与该方法的接口)。

【讨论】:

  • 什么意思?
  • 为什么投反对票?对我来说,这看起来是一种有效的方法。
  • 没有线索。是否缺少代码来支持他的答案? @MartinBackasch
【解决方案2】:

使用反射的过程很简单:找到所有感兴趣的类型t,从类型t中获取所有方法m,然后为每个m找到它的自定义属性a,然后如果集合a 包含你想要的属性,你调用方法。

另见:

看起来像这样:

foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
    foreach (Type t in a.GetTypes())
    {
        // Skip types that don't have the [Invoke] attribute
        var typeContainsInvokeAttribute = t.GetCustomAttributes(typeof(InvokeAttribute)).Any();
        if (!typeContainsInvokeAttribute)
        {
            continue;
        }

        // This throws for types without a public, parameterless constructor
        var instance = Activator.CreateInstance(t);

        foreach (var methodInfo in instance.GetType().GetMethods())
        {
            var containsInvokeAttribute = methodInfo.GetCustomAttributes(typeof(InvokeAttribute)).Any();
            if (containsInvokeAttribute)
            {
                methodInfo.Invoke(instance);
            }

        }
    }
}

【讨论】:

  • 但是当我有 20 种不同的方法分布在 20 个不同的类中时,这将如何实现?这仅涵盖所有调用方法是否在 1 个不同的类中,而不是我正在寻找的解决方案。
  • 您只需将原理提升一级:找到所有您希望检查的类型,遍历它们,实例化它们的实例并调用此代码。 Assembly.GetTypes() 做你想做的事。如果您也将相同的属性应用到该类,这将有助于快速找到感兴趣的类型。
  • 不,它没有,重新阅读我的问题。我解释说我需要在不同的类中调用不同的方法。
  • 你自己想不通吗?这个答案的每个部分之前都被问过和回答过。无论如何,我已经更新了我的答案。
  • 当然可以。只是会喜欢你的答案的一段代码。不要觉得有义务回答。
【解决方案3】:

你可以使用静态事件代替属性

public static class Events
{
    public static event EventHandler OnInvoke;

    public static void Run()
    {
        OnInvoke?.Invoke(null, EventArgs.Empty);
    }
}

在类构造函数中订阅此事件

public class Customer
{
    public Customer()
    {
        Events.OnInvoke += (sender, args) => Call();
    }
}

但是不要忘记取消订阅此事件否则您的所有对象将永远不会被释放

这将在应用程序中的每个实例化(现有)对象上调用您的代码。意味着如果您有 2 个 test1 类型的对象,那么 Console.WriteLine("test1 invoked"); 将被执行两次

【讨论】:

  • 您必须将此订阅代码添加到每个类,并自己实例化每个类。与仅致电 new Customer().Call() 相比并没有改善。
  • 可以,但您可以在一个地方添加多个订阅。如果类有多个要调用的方法。并且在原始问题中,方法不是静态的,据我所知,不应该为每种类型调用,而是为每个创建的对象调用。意味着如果创建了 10 个客户,并且在某些时候我想Invoke_all,那么每个客户都应该执行逻辑
【解决方案4】:

您可以根据您的要求创建基于接口的解决方案。

我已经修改了你的代码,并通过这种方式实现了你想要的。

namespace ConsoleApplication1
{
    public class main_class
    {
        static void Main(string[] args)
        {
            main_class.invoke_all();
        }

        public static void invoke_all()
        {
            // call all the invokes
            // Help : https://stackoverflow.com/questions/26733/getting-all-types-that-implement-an-interface

            foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes()
                 .Where(mytype => mytype.GetInterfaces().Contains(typeof(IInvokeAll))))
            {
                mytype.GetMethod("invoke").Invoke(Activator.CreateInstance(mytype, null), null);
            }

            //wait for user input
            Console.ReadLine();
        }

    }

    interface IInvokeAll
    {
        void invoke();
    }

    public class test1 : IInvokeAll
    {
        //[invoke]
        public void invoke()
        {
            Console.WriteLine("test1 invoked");
        }
    }
    public class test2 : IInvokeAll
    {
        //[invoke]
        public void invoke()
        {
            Console.WriteLine("test2 invoked");
        }
    }
}

【讨论】:

  • 但是现在每个类只能包含一个方法来调用。
  • @CodeCaster:是的,这是限制,但它仍然是正确的实现方式,我看到你的代码,它更加优化,在存储库模式中,我使用这种方式来满足与 OP 相同的要求。 .
  • @CodeCaster 这就是 BobRoss 所问的。 例如我有很多不同的类都有一个名为“invoke”的方法。
  • @Martin 是的,我肯定需要更多的咖啡。
  • @MartinBackasch,是的,并且仅基于那句话我创建了基于接口的解决方案,因为 OP 必须从所有类中调用相同的方法。
【解决方案5】:

这是一个示例代码,用于实现您使用委托的目的。

public class main_class
{
    private static main_class instance;

    public delegate void MethodInvoker();

    public MethodInvoker MyInvoker { get; set; }

    public static main_class Instance
    {
        get
        {
            if (instance == null)
            {
                instance = new main_class();
            }

            return instance;
        }
    }

    private main_class() { }

    public void invoke_all()
    {
        MyInvoker();
    }

}

public class test1
{
    public test1()
    {
        main_class.Instance.MyInvoker += invoke;
    }
    public void invoke()
    {
        Console.WriteLine("test1 invoked");
    }
}
public class test2
{
    public test2()
    {
        main_class.Instance.MyInvoker += invoke;
    }
    public void invoke()
    {
        Console.WriteLine("test2 invoked");
    }
}

所以现在,无论您调用main_class.Instance.invoke_all();,它都会调用所有方法。

【讨论】:

  • 不,它没有。您首先必须实例化您要调用invoke() 的所有类。更糟糕的是,当您无论如何都需要这样做时,调用new test1().invoke() 比将该订阅添加到每个类的构造函数更容易......
  • @CodeCaster,invoke 是一个非静态方法,所以无论如何都需要实例化该类!
  • 是的,但是如果您查看我的回答,您可以通过反射解决整个问题,而无需添加样板代码,也无需手动实例化所有类型。因为使用您的方法,如果他们添加一个新类,他们将不得不复制样板文件(或引入继承),并且不要忘记实例化新类。
  • @CodeCaster 先生,这当然是真的。但是,这将取决于 Bob 的实际世界实现。您的实现将创建实例@instance = Activator.CreateInstance(t),它将保留在该块的内部。如果 Bob 正在寻找范围之外的实例,那么实现肯定会出现一些问题。
【解决方案6】:

要调用方法,您需要实例化一个类。要实例化一个类,您需要知道类型。

所以我们需要

  1. 查找所有包含带有Invoke 属性标记的方法的类
  2. 然后实例化这些类
  3. 调用所有标记的方法。

我们先定义属性:

public class InvokeAttribute : Attribute
{
}

你可以使用这个属性来标记方法:

public class TestClass1
{
    [Invoke]
    public void Method1()
    {
        Console.WriteLine("TestClass1->Method1");
    }
    [Invoke]
    public void Method2()
    {
        Console.WriteLine("TestClass1->Method2"););
    }
}

public class TestClass2
{
    [Invoke]
    public void Method1()
    {
        Console.WriteLine("TestClass2->Method1");
    }
}

现在如何查找和调用这些方法:

var methods = AppDomain.CurrentDomain.GetAssemblies() // Returns all currenlty loaded assemblies
        .SelectMany(x => x.GetTypes()) // returns all types defined in this assemblies
        .Where(x => x.IsClass) // only yields classes
        .SelectMany(x => x.GetMethods()) // returns all methods defined in those classes
        .Where(x => x.GetCustomAttributes(typeof(InvokeAttribute), false).FirstOrDefault() != null); // returns only methods that have the InvokeAttribute

foreach (var method in methods) // iterate through all found methods
{
    var obj = Activator.CreateInstance(method.DeclaringType); // Instantiate the class
    method.Invoke(obj, null); // invoke the method
}

上面的 sn-p 将检查所有加载的程序集。 linq 查询

  1. 选择所有类型并过滤所有类
  2. 然后它会读取这些类中定义的所有方法
  3. 并检查这些方法是否标有InvokeAttribute

这为我们提供了MethodInfos 的列表。方法信息包含DeclaringType,即声明该方法的类。

我们可以使用Activator.CreateInstance 来实例化这个类的一个对象。这只有在类有一个没有参数的公共构造函数时才有效。

然后我们可以使用MethodInfo 来调用之前创建的类实例上的方法。这只有在方法没有参数时才有效。

【讨论】:

    猜你喜欢
    • 2016-09-04
    • 2012-04-09
    • 1970-01-01
    • 2019-03-30
    • 1970-01-01
    • 2018-02-05
    • 1970-01-01
    • 1970-01-01
    • 2014-04-27
    相关资源
    最近更新 更多