0

对于我正在编写的服务器,我有一个文件文件夹,每个文件中有一个类。
每个类代表来自客户端的请求操作。
如果我添加一个新动作,我只想能够添加一个新文件,让文件自己注册。

在诸如Go之类的语言中,您可以init在每个文件中拥有一个在初始化时运行的函数,让您可以注册诸如工厂委托(或第一类函数)之类的东西。

您能否在 C# 中实现类似的功能,让文件注册自己的类,而无需编辑包含所有已注册操作列表的第二个文件?

// This won't work, but how to do it?
func init() {
    // Registering a factory function to a Dictionary<string, Func<IAction>>
    Reg.ClassDictionary.Add("connect", () => { return new Connect(); });
}

namespace Action
{
    class Connect : IAction
    {
         [JsonProperty("user")]
        public string Username;

        [JsonProperty("pass")]
        public string Password;

        public bool Exec()
        {
            return ConnectToServer(Username, Password);
        }
    }
}
4

3 回答 3

2

取决于您的程序集的组织方式,但这是一种方法。定义一个名为 MyTask 的自定义属性,这样您就可以识别感兴趣的程序集中的所有类,并且您想要公开的名称例如在您的示例中为“connect”。

然后使用反射来筛选组件,找到你想要的小伙子并注册它们。

不知道你对属性和反射了解多少

但这是我的一个

[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ConversionAttribute : System.Attribute
{
    public string DatabaseName { get; set; }
    public bool ReadOnly { get; set; }
    public int ConversionOrder { get; set; }
    public String VersionStart { get; set; }
    public String VersionEnd { get; set; }
}

一个类是这样标记的

[Conversion(ConversionOrder = 14)]
public class ConversionExample : DataConversion

这个章节贯穿整个程序集,抓住任何用 ConversionAttribute 标记的东西,实例化它,然后调用一个方法。

public static void DoConversions()
{
    Type[] contents = CommandLine.Instance.TaskAssembly.GetExportedTypes();
    SortedDictionary<int, List<Type>> conversions = new SortedDictionary<int, List<Type>>();
    foreach (Type t in contents)
    {
        ConversionAttribute attr = FindAttribute(t);
        if (attr != null)
        {
            if (!conversions.ContainsKey(attr.ConversionOrder))
            {
                 conversions.Add(attr.ConversionOrder, new List<Type>());
            }
            conversions[attr.ConversionOrder].Add(t);
        }
    }

    foreach (int order in conversions.Keys)
    {
        foreach (Type t in conversions[order])
        {
            ConstructorInfo c = t.GetConstructor(new Type[] { typeof(CommandLine) });
            DataConversion d = (DataConversion)c.Invoke(new object[] { CommandLine.Instance });
            ConversionVersionStatus status = d.VersionStatus(CommandLine.Instance.TaskParameterValue("TAX_FULL_VERSION"));
            if ((status == ConversionVersionStatus.NoVersionSet) || (status == ConversionVersionStatus.Relevant))
            {
                d.Log(String.Format(CultureInfo.InvariantCulture, "Started Conversion {0}", d.FriendlyName));
                d.ExecuteTask();
                d.Log(String.Format(CultureInfo.InvariantCulture, "Finished Conversion {0}", d.FriendlyName));
           }
           else
           {
               if (status == ConversionVersionStatus.Discontinued)
               {
                   d.Log(String.Format(CultureInfo.InvariantCulture, "Conversion {0} skipped as discontinued", d.FriendlyName));
               }
               else
               {
                   d.Log(String.Format(CultureInfo.InvariantCulture, "Conversion {0} skipped as not yet relevant", d.FriendlyName));
               }
           }
       }
   }
}

所以基本上要添加一个新任务,我们在 dll 中定义它,用那个属性标记它,工作完成。

在此之后,您正在谈论某种配置文件,无论是显式配置文件,还是沿着 DI 和服务发现路线进行。

于 2012-12-03T13:01:07.410 回答
1

如果它是控制台应用程序,只需在Main方法开始时执行。

如果它是一个 Windows 服务,当你扩展时ServiceBase,你会得到一个OnStart方法(你可以覆盖),它在服务启动时执行。http://msdn.microsoft.com/en-us/library/system.serviceprocess.servicebase.onstart.aspx

这是一个关于 Windows 服务的快速教程,可帮助您入门:http ://www.switchonthecode.com/tutorials/creating-a-simple-windows-service-in-csharp

编辑:根据您的说明,AFAIK,没有内置的方法可以让文件/类型按照您的要求执行。但是,您可以做的是一些反思。您可以仔细阅读您的程序集以了解实现您的IAction(或者更确切地说,一些其他描述性类型)的特定类型/接口。您可以搜索所有这些,实例化它们,然后init在我上面描述的启动阶段调用它们的各种方法。

于 2012-12-03T12:30:07.750 回答
1

您可以在类上使用静态初始化程序。

namespace Action
{
    class Connect : IAction
    {
        static Connect ()
        {
            Reg.ClassDictionary.Add("connect", () => { return new Connect(); });
        }

        [JsonProperty("user")]
        public string Username;

        [JsonProperty("pass")]
        public string Password;

        public bool Exec()
        {
            return ConnectToServer(Username, Password);
        }
    }
}
于 2012-12-03T12:41:35.217 回答