来自用户提供类型的泛型事件生成器和处理程序

本文关键字:程序 处理 事件 泛型 用户 类型 | 更新日期: 2023-09-27 18:36:48

我正在尝试允许用户提供自定义数据并使用自定义类型管理数据。用户的算法将把时间同步事件推送到他们定义的事件处理程序中。

我不确定这是否可能,但这是我想要构建的"概念验证"代码。它不会在 for 循环中检测到 T:"找不到类型或命名空间名称'T'"

class Program
{
    static void Main(string[] args)
    {
        Algorithm algo = new Algorithm();
        Dictionary<Type, string[]> userDataSources = new Dictionary<Type, string[]>();
        // "User" adding custom type and data source for algorithm to consume
        userDataSources.Add(typeof(Weather), new string[] { "temperature data1", "temperature data2" });
        for (int i = 0; i < 2; i++)
        {
            foreach (Type T in userDataSources.Keys)
            {
                string line = userDataSources[typeof(T)][i]; //Iterate over CSV data..
                var userObj = new T(line);
                algo.OnData < typeof(T) > (userObj);
            }
        }
    }
    //User's algorithm pattern.
    interface IAlgorithm<TData> where TData : class 
    {
        void OnData<TData>(TData data);
    }
    //User's algorithm.
    class Algorithm : IAlgorithm<Weather> {
        //Handle Custom User Data
        public void OnData<Weather>(Weather data) 
        {   
            Console.WriteLine(data.date.ToString());
            Console.ReadKey();
        }
    }
    //Example "user" custom type.
    public class Weather {
        public DateTime date = new DateTime();
        public double temperature = 0;
        public Weather(string line) {
            Console.WriteLine("Initializing weather object with: " + line);
            date = DateTime.Now;
            temperature = -1;
        }
    }
}

编辑:

 string line = userDataSources[t][i]; //Iterate over CSV data..
 var userObj = Activator.CreateInstance(t);
 algo.OnData<t>(userObj);

同样的错误,但现在它在 OnData 上,所以它无法调用泛型事件,因为它无法将 T 识别为泛型类型?

来自用户提供类型的泛型事件生成器和处理程序

typeof(x)将返回由类型名称表示的Type实例 x

在您的情况下,您已经有一个Type实例;您的变量T的类型为 Type 。因此,您不需要使用typeof

string line = userDataSources[T][i];

作为关于样式的建议,没有理由使用大写T作为局部变量的名称;它是一个普通的局部变量,就像i一样,所以只需称它为tT看起来像一个通用参数,但事实并非如此。


不过,接下来的两行不会那么容易:

var userObj = new T(line);
algo.OnData < typeof(T) > (userObj);

由于T在编译时是未知的,因此您无法进行任何此类调用。您必须使用反射来执行这些调用 - 首先,使用 Activator 类创建当前引用的任何类型的实例TuserObj在编译时键入System.Object)。

随后,检索algoOnData方法的MethodInfo实例并调用MakeGenericMethod以获取插入T的泛型版本。然后,可以调用其中一个Invoke重载来实际执行OnData<T>方法。

泛型有很多事情做错了:

  1. 算法接口及其实现看起来不像您已经定义了它们。指定 type 的泛型参数后,无需在方法中再次指定它。所以这将起作用:

    //User's algorithm pattern.
    interface IAlgorithm<TData> where TData : class
    {
        void OnData(TData data);
    }
    //User's algorithm.
    class Algorithm : IAlgorithm<Weather>
    {
        //Handle Custom User Data
        public void OnData(Weather data)
        {
            Console.WriteLine(data.date.ToString());
            Console.ReadKey();
        }
    }
    

    在这种情况下,用法是:

    var userObj = Activator.CreateInstance(type, line);
    algo.OnData((Weather)userObj);
    
  2. 不能像使用通用参数那样使用密钥信息。泛型参数是编译时,类型实例 - 运行时。通过反思,有办法,但它们是丑陋和低效的。所以,没有新的T(线)。此外,即使你在这里有正确的泛型参数,你也只能使用默认构造函数创建它,并且只有在你对参数有 new() 约束的情况下。

  3. 没有算法。OnData(userObj),请参阅上一个项目符号。

我向您提出以下建议:

    //User's algorithm pattern.
    interface IAlgorithm
    {
        void OnData(object data);
    }
    abstract class BaseAlgorithm<TData> : IAlgorithm where TData : class
    {
        public void OnData(object data)
        {
            //perform type checks here, if necessary
            OnData(data as TData);
        }
        protected abstract void OnData(TData data);
    }
    //User's algorithm.
    class Algorithm : BaseAlgorithm<Weather>
    {
        //Handle Custom User Data
        protected override void OnData(Weather data)
        {
            Console.WriteLine(data.date.ToString());
            Console.ReadKey();
        }
    }

感谢 @O.R 映射器和@galenus :: 使用两个答案的组合,这很优雅,不需要太多思考。通过添加的假设,有一个包含日期时间的基本数据类 - 跨所有时间序列数据的通用索引。

class Program
{
    static void Main(string[] args)
    {
        IAlgorithm algo = new Algorithm();
        Dictionary<Type, string[]> userDataSources = new Dictionary<Type, string[]>();
        userDataSources.Add(typeof(Weather), new string[] { "temperature data1", "temperature data2" });
        for (int i = 0; i < 2; i++)
        {
            foreach (Type t in userDataSources.Keys)
            {
                string line = userDataSources[t][i]; //Iterate over CSV data..
                var userObj = Activator.CreateInstance(t);
                UserData castObj = (UserData)userObj;
                castObj.Constuctor(line);
                algo.OnData(castObj);
            }
        }
    }
    interface IAlgorithm
    {
        void OnData(object data);
    }
    abstract class BaseAlgorithm<TData> : IAlgorithm where TData : class 
    {
        public void OnData(object data) 
        {
            //perform type checks here, if necessary
            OnData(data as TData);
        }
        protected abstract void OnData(TData data);
    }
    //User's algorithm.
    class Algorithm : BaseAlgorithm<Weather>
    {
        //Handle Custom User Data
        protected override void OnData(Weather data)
        {
            Console.WriteLine(data.date.ToString());
            Console.ReadKey();
        }
    }
    public abstract class UserData {
        public DateTime date;
        public UserData() { }
        public abstract void Constuctor(string line);
    }
    public class Weather : UserData 
    {
        public DateTime date = new DateTime();
        public double temperature = 0;
        public Weather() { }
        public override void Constuctor(string line) {
            Console.WriteLine("Initializing weather object with: " + line);
            date = DateTime.Now;
            temperature = -1;
        }
    }
}