none
Abrast or interface static property RRS feed

  • Question

  • Hi experts,

    I'm implementing a parser using regular expressions and I have different type of rows that i need to analyze differently.

    eg:
    LINE 16,0,0,0
    LINE 1,1,0,0,-1,"9052"
    LINE 1,0,1,0,-1,"9046"
    LINE 8,0,0,1,0,0,4000,0,0,
        "1","2","3"
    LINE 11,0,0,0,4,
        "1","2","T4V7Z3","5Y6"
    LINE 7,0,0,2,0
    LINE 7,0,0,3,0
    LINE 7,0,0,4,0
    LINE 7,0,0,5,0

    To do so my idea was to implement different classes using the same interface and/or abstract class (IRow and/or ARow) and to create the correct one using a factory method. To choose which class instantiate I thought about using a different const called Regex with different implementations in each of the differect classes implementing the interface or abstract class and checking it using Reflection to get the const string and then call the constructor.

    public TMACParser(string filename) : base(filename)
    {
    	_iMacroRowsTypes = new Dictionary<string, ConstructorInfo>();
    
    	//I get all types implementing the correct interface or abstract class
    	List<Type> iMacroRowsType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(x => x.GetTypes()).Where(x => typeof(IMacroRow).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract).ToList();
    
    	foreach(Type type in iMacroRowsType)
    	{
    		//I save the regex string and the constructor so i can check the regex and eventually call the associated constructor
    		_iMacroRowsTypes.Add((string)type.GetField("Regex").GetValue(null), type.GetConstructor(new Type[] { typeof(Match) }));
    	}
    

    The problem is that I don't know how to force this inside the interface or the abstract class and I need a static property to check it before instantiating the correct object.

    I know I can implement a specific const inside each of the subclasses but it's not very clean as a method. Is there another way?

    Monday, March 2, 2020 12:04 PM

Answers

  • I have changed the approach.

    Now i parse the common part of the LINE statement in the factory method and demand the extra part of the string to the right constructor.

    IMacroRow GetMacroRow(string row)
    {
    	const string GenericRowRegex = @"LINE (?<type>\d*),(?<isCommented>\d),(?<isConditioned>\d)";
    
    	Regex regex = new Regex(GenericRowRegex);
    	Match match = regex.Match(row);
    
    	if (match.Success)
    	{
    		MacroRowType type = (MacroRowType)int.Parse(match.Groups["type"].Value);
    		bool isCommented = Convert.ToBoolean(int.Parse(match.Groups["isCommented"].Value));
    		bool isConditioned = Convert.ToBoolean(int.Parse(match.Groups["isConditioned"].Value));
    
    		string specificAttributes = row.Substring(match.Index, match.Length);
    
    		switch(type)
    		{
    			case MacroRowType.Endloop:
    				return new EndLoopRow(isCommented, isConditioned);
    
    			case MacroRowType.Return:
    				return new ReturnRow(isCommented, isConditioned);
    
    			case MacroRowType.Terminate:
    				return new TerminateRow(isCommented, isConditioned);
    
    			case MacroRowType.Comment:
    				return new CommentRow(isCommented, isConditioned, specificAttributes);
    		}
    	}
    
    	return null;
    }

    • Marked as answer by ACalafiore Wednesday, March 4, 2020 8:00 AM
    Tuesday, March 3, 2020 8:13 AM

All replies

  • If you need data before creating a type then use an attribute. You can then retrieve the attribute data and figure things out. It would replace your "static property" that you are talking about. But note that RE may not be sufficient in which case this won't work anymore.

    [Parser("abc")]
    public class MyLineParser
    {
    }
    
    [Parser("def")]
    public class YourLineParser
    {
    }

    Personally I question whether this is the correct approach at all. I might recommend that you take a step back and write a general line parser to handle the lines and then have it be smart enough to figure out whether it needs to defer to something more specific. That could be a combination of RE and other logic if needed. 

    I also wonder about your use of reflection for the constructors. A factory class might be better and you should probably use a single instance of the line-specific parsers instead of a separate parser for each line. Create the parser for a line type when you need it and have each parser be stateless so you can use the same parser instance to read all the data. This would reduce the # of calls using reflection and probably make it easier to understand.


    Michael Taylor http://www.michaeltaylorp3.net

    Monday, March 2, 2020 6:58 PM
    Moderator
  • I have changed the approach.

    Now i parse the common part of the LINE statement in the factory method and demand the extra part of the string to the right constructor.

    IMacroRow GetMacroRow(string row)
    {
    	const string GenericRowRegex = @"LINE (?<type>\d*),(?<isCommented>\d),(?<isConditioned>\d)";
    
    	Regex regex = new Regex(GenericRowRegex);
    	Match match = regex.Match(row);
    
    	if (match.Success)
    	{
    		MacroRowType type = (MacroRowType)int.Parse(match.Groups["type"].Value);
    		bool isCommented = Convert.ToBoolean(int.Parse(match.Groups["isCommented"].Value));
    		bool isConditioned = Convert.ToBoolean(int.Parse(match.Groups["isConditioned"].Value));
    
    		string specificAttributes = row.Substring(match.Index, match.Length);
    
    		switch(type)
    		{
    			case MacroRowType.Endloop:
    				return new EndLoopRow(isCommented, isConditioned);
    
    			case MacroRowType.Return:
    				return new ReturnRow(isCommented, isConditioned);
    
    			case MacroRowType.Terminate:
    				return new TerminateRow(isCommented, isConditioned);
    
    			case MacroRowType.Comment:
    				return new CommentRow(isCommented, isConditioned, specificAttributes);
    		}
    	}
    
    	return null;
    }

    • Marked as answer by ACalafiore Wednesday, March 4, 2020 8:00 AM
    Tuesday, March 3, 2020 8:13 AM
  • Hi ACalafiore,
    I am glad you have got your solution. We appreciated you shared us your solution. And we also hope you can mark it as an answer. By marking a post as Answered, you help others find the answer faster.
    Best Regards,
    Daniel Zhang


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, March 4, 2020 2:00 AM