views:

160

answers:

4

I have a class that parses in data from a comma delimited text file. I have an enum for the fields to help me parse data in easier. The class that parses all the records in holds public variables for each field, and of course their variable types. I need to get the type of these variables based on the enum given.

public enum DatabaseField : int
    {
        NumID1 = 1,
        NumID2 = 2,
        NumID3 = 3,
    };

public class DataBaseRecordInfo
    {
        public long NumID1 { get; set; }
        public int NumID2 { get; set; }
        public short NumID3 { get; set; }

        public static Type GetType(DatabaseField field)
        {
           Type type;

           switch (field)
           {
               case DatabaseField.NumID1:
                   type = typeof(long);
                   break;
               case DatabaseField.NumID2:
                   type = typeof(int);
                   break;
               case DatabaseField.NumID3:
                   type = typeof(short);
                   break;
               default:
                   type = typeof(int);
                   break;
           }

           return type;
        }
     };

NumID1, NumID2, NumID3 all get assigned within my constructor. However, I want to get these types without ever creating an instance of DataBaseRecordInfo. Right now my static method above would work, however, if I wanted to change the variable type, I would have to change it in 2 places. Is there a way to get around having to change this in both places and keep it as a static method?

A: 

Yes, you can use the names in the enum together with reflection on the DatabaseRecordInfo type to get the types you need.

This could be done like this:

public class DataBaseRecordInfo
{
    public long NumID1 { get; set; }
    public int NumID2 { get; set; }
    public short NumID3 { get; set; }

    public static Type GetType(DatabaseField field)
    {
        string name = field.ToString();
        Type recordType = typeof (DataBaseRecordInfo);
        var props = recordType.GetProperties();
        var matchedProperty = props.Where(p => name == p.Name).FirstOrDefault();
        if (matchedProperty == null)
            return null;    // We do not have a matching property.
        return matchedProperty.PropertyType;
    }
};

You will probably want to cache the result in a dictionary, since the reflection can be expensive performance-wise.

driis
+4  A: 

If the name is always going to match exactly you can do this using reflection.

return typeof(DataBaseRecordInfo)
    .GetProperty(field.ToString(), BindingFlags.Public | BindingFlags.Instance)
    .PropertyType;

You could even cache these values in a dictionary, so if found, just return the dictionary entry, otherwise determine using reflection and cache the result.

David M
+1 I thought about the dictionary answer too, but I like the reflection way better
SwDevMan81
This hybrid approach ticks all the boxes IMHO
David M
Haha, what? Ticks all the boxes, I must have missed something... couldnt even find this one in The New Hacker's Dictionary, maybe a time for a revision?
SwDevMan81
A: 

How about something like this:

public static Type GetType(DatabaseField field)
{
  DataBaseRecordInfo dbri = new DataBaseRecordInfo();

  switch (field)
  {
    case DatabaseField.NumID1:
      return dbri.NumID1.GetType(); 
    case DatabaseField.NumID2:
      return dbri.NumID2.GetType(); 
    case DatabaseField.NumID3:
     return dbri.NumID3.GetType(); 
    default:
      return typeof(int);
  }
}

I know you said without ever having to create an instance of DataBaseRecordInfo but I'm assuming you meant an instance outside of the static method. No one ever sees this instance.

_rusty
This creates an instance every time the method is called. It also doesn't solve the maintaining stuff in two places problem.
David M
This would not work for a number of reasons..
jsmith
I got the impression that the "maintaining stuff in two places" problem was limited to the types, which this addresses pretty well. It even gives you the flexibility of using mismatched names.
Sapph
David, you could create a single static instance in the static constructor to avoid creating an instance on each call. Also, not sure how this isn't DRY -- care to explain?
_rusty
@jsmith - which are ...? It works, I just don't think it's a very good solution to the problem.
David M
@David M I think reflection is what I was looking for. For design reasons I would rather not have a default constructor, meaning I pass another class to this one for parsing, so to declare an instance with a default constructor would not be possible. I could create one of course, but would rather not, seems sloppy. So yes, it does work, but it is not what I am looking for.
jsmith
Fair enough. I also prefer reflection, just couldn't see a problem with this code - but the default constructor issue is a fair comment.
David M
A: 

If you want to bind enum value with some additional information you can use your own CustomAttribute.

Maybe you need something like this:

public class DatabaseTypeAttribute : Attribute
{
    public DatabaseTypeAttribute(Type type)
    {
        Type = type;
    }
    public Type Type { get; private set; }
}

public enum DatabaseField : int
{
    [DatabaseType(typeof(long))]
    NumID1 = 1,
    [DatabaseType(typeof(int))]
    NumID2 = 2,
    [DatabaseType(typeof(short))]
    NumID3 = 3,
    NumID4 = 4,
};

public static class DatabaseFieldHelper
{
    public static Type GetDatabaseType(this DatabaseField field)
    {
        var attributes = (DatabaseTypeAttribute[])typeof(DatabaseField).GetField(Enum.GetName(typeof(DatabaseField), field))
            .GetCustomAttributes(typeof(DatabaseTypeAttribute), false);
        if (attributes.Length == 0)
            return typeof(int); //returns default type
        return attributes[0].Type;

    }
}

//prints: NumID1 database type: System.Int64
Console.WriteLine("NumID1 database type: {0}", DatabaseField.NumID1.GetDatabaseType());

//prints: NumID2 database type: System.Int32
Console.WriteLine("NumID2 database type: {0}", DatabaseField.NumID2.GetDatabaseType());

//prints: NumID3 database type: System.Int16
Console.WriteLine("NumID3 database type: {0}", DatabaseField.NumID3.GetDatabaseType());

//prints: NumID4 database type: System.Int32
Console.WriteLine("NumID4 database type: {0}", DatabaseField.NumID4.GetDatabaseType());
Sergey Teplyakov