3
\$\begingroup\$

I've been doing some parsing with regular expression named capture groups and decided it might make sense to write an extension to handle this.

The code below will create an instance of a specified type and attempt to match the property names to the capture group names and then set the values. It also attempts to cast between compatible types.

Any suggestions for improving this code? Is it ok as an extension?

 public static class RegularExpressionExtension
{
    public static T CreateType<T>(this Regex regEx, string matchString) where T : new()
    {
        MatchCollection matchCollection = regEx.Matches(matchString);
        T obj = new T();
        Type type = typeof(T);
        PropertyInfo[] properties = type.GetProperties();

        IEnumerable<string> groupNames = regEx.GetGroupNames().Skip(1); //First Group is always 0
        if (matchCollection.Count >= 1)
        {
            SetPropertyValuesFromGroupMatches<T>(groupNames, regEx, obj, matchCollection[0].Groups, properties);
        }
        return obj;
    }

    public static IEnumerable<T> CreateTypeCollection<T>(this Regex regEx, string matchString) where T : new()
    {
        MatchCollection matchCollection = regEx.Matches(matchString);
        IEnumerable<string> groupNames = regEx.GetGroupNames().Skip(1); //First Group is always 0
        Type type = typeof(T);
        PropertyInfo[] properties = type.GetProperties();

        foreach (Match match in matchCollection)
        {
            T obj = new T();
            SetPropertyValuesFromGroupMatches<T>(groupNames, regEx, obj, match.Groups, properties);
            yield return obj;
        }
    }

    private static void SetPropertyValuesFromGroupMatches<T>(IEnumerable<string> groupNames, Regex regEx, T typeToPopulate, GroupCollection groupCollection, PropertyInfo[] properties)
    {
        Type type = typeof(T);

        foreach (string group in groupNames)
        {
            var foundProperty = properties.SingleOrDefault(p => p.Name.Equals(group,StringComparison.CurrentCultureIgnoreCase));
            if (foundProperty != null)
            {
                var matchValue = groupCollection[group].Value;
                object convertedValue = System.ComponentModel.TypeDescriptor.GetConverter(foundProperty.PropertyType).ConvertFrom(matchValue);
                foundProperty.SetValue(typeToPopulate, convertedValue, null);
            }
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Overloads that just take the match pattern and instantiate their own Regex (or just use the static methods) might be called for. The code also requires using only mutable properties for all data members; you can't have any get-only properties, and you can't use fields (readonly or otherwise) which may hurt you if you want to use this down the road for a class with calculated or WORM properties. \$\endgroup\$ – KeithS Jul 17 '12 at 15:21
2
\$\begingroup\$

If you are doing reflection magics the caller of your method may be as well. If that person already has a Type it can be a bit of a pain in the butt to call your generic method. Since you are already working against a Type offering an overload for it should not be too much work. Perhaps extracting the following code into its own method may help:

PropertyInfo[] properties = type.GetProperties();
IEnumerable<string> groupNames = regEx.GetGroupNames().Skip(1); //First Group is always 0
if (matchCollection.Count >= 1) {
    SetPropertyValuesFromGroupMatches<T>(groupNames, regEx, obj, matchCollection[0].Groups, properties);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.