用户提供的类型的通用事件生成器和处理程序?

时间:2013-10-23 20:48:42

标签: c# events c#-4.0 generics types

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

我不确定这是否可行,但这里是我想要构建的“概念证明”代码。它在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作为泛型类型?

3 个答案:

答案 0 :(得分:1)

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

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

string line = userDataSources[T][i];

作为关于样式的建议,没有理由使用大写T作为本地变量的名称;这是一个正常的局部变量,就像i一样,所以只需称它为tT看起来像一个通用参数,它不是。


接下来的两行并不那么容易:

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

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

随后,检索algo的{​​{1}}方法的MethodInfo实例,并调用MakeGenericMethod以获取插入了OnData的通用版本。然后,您可以调用one of the Invoke overloads来实际执行T方法。

答案 1 :(得分:1)

对于泛型,你做了很多事情:

  1. 算法接口及其实现看起来不像你已定义它们。指定类型的泛型参数后,您不必在方法中再次指定它。所以这将有效:

    //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() constraint时才会创建。

  3. algo.OnData&lt; t&gt;(userObj),请参阅上一个项目符号。

  4. 我建议你:

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

答案 2 :(得分:0)

感谢@ O.R Mapper和@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;
        }
    }
}