简体   繁体   English

C#单个集合中的多个泛型类型

[英]C# Multiple generic types in single collection

There are a lot of answers for this using a single Type with interfaces and abstract classes which all work fine for one generic T value. 使用具有接口和抽象类的单个Type,对于一个通用T值都可以正常工作,对此有很多答案。 What I am trying to achieve I have not seen and am wondering if anyone has an idea. 我想要实现的目标我还没有看到,并且想知道是否有人有想法。

Scenario 情境

    public class Field<T>
    {
        public Expression<Func<T,object>> FieldName { get; set; }
    }

    public class FValue<T, F> : Field<T>
    {

        public F FieldValue { get; set; }
    }

//Test

var fieldList = new List<Field<Person>>();
fieldList.Add(new FValue<Person, DateTime> { FieldName=x=>x.SomeDate, FieldValue=DateTime.Now });
fieldList.Add(new FValue<Person, string> { FieldName=x=>x.SomeData, FieldValue="test" });

Ideally i want to do the following:- The list will contain the same type for T, the type for F will change to various types like date,string etc. 理想情况下,我想执行以下操作:-列表将包含T的相同类型,F的类型将更改为各种类型,例如日期,字符串等。

When iterating over the list i want both the FieldName and FieldValue 当遍历列表时,我想要FieldName和FieldValue

I can't start the list using new <List<FValue<Persion,string>>() ; 我无法使用新的<List<FValue<Persion,string>>()开始列表; for the obvious reason that all F values will have to be string. 显而易见的原因是所有F值都必须是字符串。

Also when obtaining the FieldName somehow the value should be casted to Expression<Func<T,F>> . 同样,在获取FieldName时,应将值强制转换为Expression<Func<T,F>>

Any suggestions would be appreciated. 任何建议,将不胜感激。

Whenever you want to use generics you need a specific reason to use it. 每当您要使用泛型时,都需要有特定的理由使用它。 See this answer about when to use Generics What is cool about generics, why use them? 关于何时使用泛型的信息,请参见此答案。泛型的优点是什么,为什么要使用泛型?

As you can see in the link, one of the main reason is to have type-safe properties. 如您在链接中看到的,主要原因之一是具有类型安全的属性。 This also means that your class will be limited to the specific type. 这也意味着您的课程将限于特定的类型。 And taking this in consideration, the usages of your class will be limited. 考虑到这一点,该类的用法将受到限制。

Here is how I could use your class with limited usages that don't require (boxing/unboxing), but still requires casting 这是我可以在不需要(装箱/拆箱)但仍需要强制转换的有限用法下使用您的班级的方法

private static void UseFieldList<T>(List<Field<T>> fieldList)
{
    foreach (var field in fieldList)
    {
        var propName = field.FieldNameText;
        var textField = field as FValue<T, string>;
        if (textField != null)
        {
            // Now can use string specific functions without (boxing/unboxing) 
            Console.WriteLine(propName + " " + textField.FieldValue );
            continue;
        }
        var dateField = field as FValue<T, DateTime>;
        if (dateField != null)
        {
            // Now can use date specific functions without (boxing/unboxing) 
            Console.WriteLine(propName + " " + dateField.FieldValue.ToShortDateString());
            continue;
        }

        throw new NotSupportedException("The type of the field is not supported: " + field.GetType().Name);
    }
}

To get the name out of a expression you can see the answer here Retrieving Property name from lambda expression 要从表达式中获取名称,您可以在此处查看答案从lambda表达式中检索属性名称

And to use this I would change the way you are creating the objects to something similar to the usages of Tuple: 为了使用它,我将把创建对象的方式更改为类似于Tuple的用法:

// Newer code storing name 
fieldList.Add(FValue<Person>.Create(x => x.DateOfBirth, DateTime.Now ));
fieldList.Add(FValue<Person>.Create(x => x.Name, "test"));

// Old code storing expression instead of name 
fieldList.Add(new FValue<Person, DateTime> { FieldName = x => x.DateOfBirth, FieldValue = DateTime.Now });
fieldList.Add(new FValue<Person, string> { FieldName = x => x.Name, FieldValue = "test" });

// Not supported Type Int 
fieldList.Add(new FValue<Person, int> {FieldName = x => x.Name, FieldValue = 12});

And here is the factory class 这是工厂课

public class FValue<T>
{
    public static Field<T> Create<F>(Expression<Func<T, F>> fieldNameExpression, F value)
    { 
        return new FValue<T, F>
        {
            FieldNameText = GetPropertyInfo(fieldNameExpression).Name,
            FieldValue = value
        };
    }
}

Results of the console: 控制台结果:

DateOfBirth 1/19/2017
Name test

x => Convert(x.DateOfBirth) 1/19/2017
x => x.Name test

The type of the field is not supported: FValue`2

I'm not sure I see a way around this one without using Reflection. 我不确定在不使用反射的情况下是否可以解决此问题。 Using these classes: 使用这些类:

public class Field<T>
{
    public Expression<Func<T, object>> FieldName { get; set; }
}

public class FValue<T, F> : Field<T>
{
    public F FieldValue { get; set; }
}

You can iterate over them like so: 您可以像这样遍历它们:

var list = new List<Field<string>>();
list.Add(new FValue<string, int>() { FieldName = null, FieldValue = 5 });

foreach (var x in list)
{
    Type[] types = x.GetType().GetGenericArguments();

    // Dirty check to confirm this is an FValue not a Field
    if (types.Length == 2)
    {
        var fieldName = x.FieldName;
        object fieldValue = x.GetType().GetProperty("FieldValue").GetValue(x);
        // fieldValue will be "5"
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM