将泛型类型转换为子类

时间:2017-04-13 12:42:17

标签: c# generics

我从泛型类型开始,我一直坚持我的项目。也许我不太了解仿制药。说明已插入内联。基本上我需要实现Do()方法,但我不知道如何解决< T2>:

    public abstract class MyGenericClass<T> { }

    public class MyGenericClass<T, T2> : MyGenericClass<T>
    {
        public Expression<Func<T, T2>> expression;

        public MyGenericClass(Expression<Func<T, T2>> expression)
        {
            this.expression = expression;
        }
    }

    public class MyClass<T>
    {
        // I need to mantain a list of my generic class for later use.
        // I don't know T2 at this point.
        // So I Chose to use Inheritance as a workaround (MyGenericClass<T> and MyGenericClass<T, T2>).
        // Maybe it's not a good solution but I counldn't find out other solution.
        public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>();

        // I receive the parametric argument T2 here as part of an Expresion.
        // And I keep the expression in my list.
        public MyGenericClass<T, T2> ReceivingMethod<T2>(Expression<Func<T, T2>> expression)
        {
            MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression);
            MyGenericList.Add(genericImp);
            return genericImp;
        }
    }

    public class Client<T>
    {
        MyClass<T> class1;

        // class1 has been created and his field MyGenericList has beed populated.
        // Then when I call Do()....
        public void Do()
        {
            foreach (var item in class1.MyGenericList)
            {
                // ...I need something like this here, 
                // but it does not compile because I don't know T2 here.
                // The caller of Do() method doesn't know T2.
                MyGenericClass<T, T2> myGenericItem = (MyGenericClass<T, T2>)item;
                var a = myGenericItem.expression;
            }

        }
    }

2 个答案:

答案 0 :(得分:1)

您必须以某种方式提供Do() T2参数。所以我的解决方案是创建一个相同类型的方法参数。我还嵌套了类型,以确保它们都引用相同的T

我还将参数重命名为更具描述性的

//  T  -> TArg
//  T2 -> TResult
public abstract class MyBaseClass<TArg>
{
    public class MyExpressionClass<TResult> : MyBaseClass<TArg>
    {
        public Expression<Func<TArg, TResult>> Expression { get; private set; }
        public MyExpressionClass(Expression<Func<TArg, TResult>> expression)
        {
            this.Expression=expression;
        }
    }

    public class MyCollectionClass 
    {
        public List<MyBaseClass<TArg>> MyGenericList = new List<MyBaseClass<TArg>>();

        public MyExpressionClass<TResult> ReceivingMethod<TResult>(Expression<Func<TArg, TResult>> expression)
        {
            var genericImp = new MyExpressionClass<TResult>(expression);
            MyGenericList.Add(genericImp);
            return genericImp;
        }
    }

    public class Client
    {
        public MyCollectionClass List = new MyCollectionClass();

        public void Do<TResult>()
        {
            foreach(var item in List.MyGenericList)
            {
                var expr = item as MyExpressionClass<TResult>;
                if(expr!=null)
                {
                    var a = expr.Expression;
                    Console.WriteLine(a);
                }
            }
        }
    }
}



class Program
{
    static void Main(string[] args)
    {
        var client = new MyBaseClass<int>.Client();
        // add conversion expressions
        client.List.ReceivingMethod((i) => (i).ToString());
        client.List.ReceivingMethod((i) => (2*i).ToString());
        client.List.ReceivingMethod((i) => (3*i).ToString());

        // The programmer has to manually enforce the `string` type
        // below based on the results of the expressions above. There
        // is no way to enforce consistency because `TResult` can be 
        // _any_ type. 
        client.Do<string>();

        // Produces the following output
        //
        // i => i.ToString()
        // i => (2*i).ToString()
        // i => (3*i).ToString()
    }
}

答案 1 :(得分:0)

解决方案1 ​​受到@KMoussa评论的启发。我使用抽象方法将责任委托给MyGenericClass。这似乎是一个更好的设计。所有子类都将实现此方法(DoTheWork())。并且可以从我的Client.Do()方法调用,只有T param:

    public abstract class MyGenericClass<T>
    {
        public abstract string DoTheWork();
    }

    public class MyGenericClass<T, T2> : MyGenericClass<T>
    {
        public override string DoTheWork()
        {
            // .... I can use expression here
        }

        private Expression<Func<T, T2>> expression { get; set; }

        public MyGenericClass(Expression<Func<T, T2>> expression)
        {
            this.expression = expression;
        }
    }

    public class MyClass<T>
    {
        public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>();
        public void ReceivingMethod<T2>(Expression<Func<T, T2>> expression)
        {
            MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression);
        }
    }

    public class Client<T>
    {
        MyClass<T> class1;
        public void Do()
        {
            // I don't need to cast to MyGenericClass<T, T2>
            foreach (MyGenericClass<T> myGenericItem in class1.MyGenericList)
            {
                string result = myGenericItem.DoTheWork();
            }

        }
    }

解决方案2 灵感来自@ ja72和@juharr评论。用反射。首先,我使用abstract属性在MyGenericClass上保存类型T2。然后我可以使用反射调用泛型方法(MethodWithArgument),这样我就可以为该转换引入参数:

public abstract class MyGenericClass<T>
{
    public abstract Type type { get; set; }
}

public class MyGenericClass<T, T2> : MyGenericClass<T>
{
    public Expression<Func<T, T2>> expression { get; set; }

    public MyGenericClass(Expression<Func<T, T2>> expression)
    {
        type = typeof(T2);  // I save the type of T2
        this.expression = expression;
    }
}

public class MyClass<T>
{
    public List<MyGenericClass<T>> MyGenericList = new List<MyGenericClass<T>>();
    public void ReceivingMethod<T2>(Expression<Func<T, T2>> expression)
    {
        MyGenericClass<T, T2> genericImp = new MyGenericClass<T, T2>(expression);
    }
}

public class Client<T>
{
    MyClass<T> class1;
    public void Do()
    {
        foreach (MyGenericClass<T> myGenericItem in class1.MyGenericList)
        {
            MethodInfo method = GetType().GetMethod("MethodWithArgument");
            MethodInfo generic = method.MakeGenericMethod(new Type[] { myGenericItem.type });
            string g = (string)generic.Invoke(this, new object[] { myGenericItem });
        }
    }

    // I introduce T2 in this method
    public string MethodWithArgument<T2>(MyGenericClass<T> myClass)
    {
        // Now, the casting is valid
        MyGenericClass<T, T2> mySubClass = (MyGenericClass<T, T2>)myClass;
        var a = mySubClass.expression;
        // ... I can work with expression here
    }
}