0

I have some text files that are delimited by commas and I want to read a line, then instantiate it and assign values to the properties. The number of text files will grown in the future but now, I only need to work with a handful of them.

So I created a base class that'll take in a FileInfo argument, but the problem is how do I assign values to the instance? At the base class, it won't know what the properties names are. I think I should iterate through the properties and assign them by index, but t.GetType().GetProperties() doesn't return any items.

public class AccountDataFile : DataFileBase<AccountDataFile.Account>
{
    public class Account
    {
        public string Name;
        public string Type;
    }

    public AccountDataFile(FileInfo fiDataFile) : base(fiDataFile) { }
}

base class:

public class DataFileBase<T> where T : new()
{
    public List<T> Data;

    public DataFileBase(FileInfo fi)
    {
        this.Data = new List<T>();

        var lines = fi.ReadLines();

        foreach (var line in lines)
        {
            var tokens = line.Split(CONSTS.DELIMITER);
            var t = new T();

            // how to assign values to properties?

            this.Data.Add(t);
        }
    }
}
Ray Cheng
  • 12,230
  • 14
  • 74
  • 137

3 Answers3

3

Make the inheriting class provide the implementation:

public abstract class DataFileBase<T>
{
    protected abstract T BuildInstance(string[] tokens);
}

public AccountDataFile : DataFileBase<AccountDataFile.Account>
{
    protected override Account BuildInstance(string[] tokens)
    {
        var account = new Account();
        account.Name = tokens[0]; // or whatever

        return account;
    }
}
Trevor Pilley
  • 16,156
  • 5
  • 44
  • 60
  • That's what I wanted to avoid. I would hope I can have a single implementation instead of many. – Ray Cheng May 12 '14 at 16:30
  • @RayCheng This is exactly what I was going to suggest, and it's the cleanest and proper way to solve your problem. If you want to avoid having to repeat this over and over again, you *could* make the base class use reflection to assign tokens to properties (seems flaky IMO), but make it virtual so derived classes can still define their own behaviour if need be. – dcastro May 12 '14 at 16:33
  • @dcastro, but `GetProperties` didn't return any fields, so I can't use reflection to assign values. Any ideas why? But in the debugger, I can see the reflected object and its fields. – Ray Cheng May 12 '14 at 16:35
  • 1
    @RayCheng That's because `Account` has no properties :) It has 2 fields however, so use `GetFields` instead. Properties and fields are *not* the same thing. – dcastro May 12 '14 at 16:42
1

You could add an abstract method to the base class to create the right kind of object. In your DataFileBase add a method like:

public abstract T CreateObject();

And implement it in AccountDataFile :

public override AccountDataFile.Account CreateObject() { new AccountDataFile.Account(); }
Chris Hinton
  • 866
  • 5
  • 15
1

Consider existing CSV parser/reader for C#? .

If you still want to get your own - many serializers use attributes to do property to field name/column matches. I.e. annotate your Account type with ColumnAttribute, or similar custom attribute and read values at run-time. MSDN have article Accessing Attributes by Using Reflection covering reading attributes.

// starting point to read attributes:
System.Attribute[] attrs = System.Attribute.GetCustomAttributes(myType);  
Community
  • 1
  • 1
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179