-1

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. 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.

When iterating over the list i want both the FieldName and FieldValue

I can't start the list using new <List<FValue<Persion,string>>(); for the obvious reason that all F values will have to be string.

Also when obtaining the FieldName somehow the value should be casted to Expression<Func<T,F>>.

Any suggestions would be appreciated.

Andrew Shepherd
  • 44,254
  • 30
  • 139
  • 205
AliK
  • 962
  • 2
  • 10
  • 31
  • All `FValue<>` types have to have something in common in order to be used in a typesafe collection. Otherwise just use `ArrayList`. – John Alexiou Jan 20 '17 at 05:29
  • 1
    If your F values are just `int`, `double`, `string` etc. you could store them all as `IConvertible` which includes some nice conversion capabilities. What do you actually want to DO with the values once you retrieve them? – Ian Mercer Jan 20 '17 at 05:34
  • It seems you are trying to create something like reflections (lists of fields with values). Unfortunately without more details this question cannot be answered. This question suffers from the [XY Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). – John Alexiou Jan 20 '17 at 06:07

2 Answers2

4

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

And to use this I would change the way you are creating the objects to something similar to the usages of 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
Community
  • 1
  • 1
AL - Lil Hunk
  • 985
  • 6
  • 9
0

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"
    }
}
Abion47
  • 22,211
  • 4
  • 65
  • 88
  • Yes its an idea, but still Fieldname will return object instead of the type of F. – AliK Jan 20 '17 at 04:37
  • 1
    @AliK FieldName will return its value. It happens to be stored in an `object` variable, but you can do some validation to find out what type it is and proceed accordingly. If, however, you're looking for a way to get it like essentially `[insert type here] value = x.FieldValue;`, then you're out of luck because such a method requires knowing at compile time what type `FieldValue` is going to be, which you can't know when generics are involved. – Abion47 Jan 20 '17 at 04:49
  • You are correct and this is why was thinking of some kind of store like a dictionary or something else we you can store the type and pull it back and pass it out as required but its one of those things that would be more time consuming so i guess i'll have to keep trying. – AliK Jan 20 '17 at 05:04
  • @Alik So you want some souped up Expando? – Martheen Jan 20 '17 at 05:05
  • @AliK Well I mean if that's the direction you are going, you could always create a `Dictionary>` type deal for storing specific actions for specific types. You could then use it like `actionDict[types[2]](fieldValue);`. – Abion47 Jan 20 '17 at 05:20