Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to support multiple custom types?

Tags:

c#

I have a Types project where I define custom class objects that I want to work on in my main application. The objects are basically derived from strings and parsed into a structure.

I have two problems

1 - In a separate project I have a File reader class where I scan text files for the string types I have defined. For example by regular expression. Currently I added my Types project as a project reference and I just list the regular expressions at the top of my read class. When i find a type I convert the string to the appropriate type. However how can i improve this so that is it directly connected to my Types project - so when i update it with new types the Read class knows that it should support the new types?

2 - I'm trying to create a DLL that works on these specific types after they are read from the text file. How do I tell my DLL that I want to support the types in my Types project? Do I have to make an overloaded function for each type I want to work on? Do I use an interface?

Any advice is greatly appreciated.

EDIT: Added example code of what I''m trying to do

//PROJECT 1 - handles IO operation like Reading and writing
//function in read class job is to find one of several predefined string types by regular expression...once found they are converted to the data structure (by passing string to constructor of type class defined in the other project

 public class Read
{
    public string[] FileList { get; set; }

    private static Int64 endOffset = 0;
    private FileStream readStream;
    private StreamReader sr;

    private System.Text.RegularExpressions.Regex type1 = new System.Text.RegularExpressions.Regex(@"@123:test");
    private System.Text.RegularExpressions.Regex type2 = new System.Text.RegularExpressions.Regex(@"TESTTYPE2");

    public Read(string[] fl)
    {
        FileList = fl;
    }

    public object ReturnMessage(FileStream readStream, out int x)
    {
        //readStream = new FileStream(file, FileMode.Open, FileAccess.Read);
        x = 0;
        //endOffset = 0;
        bool found = false;
        char ch;
        string line = string.Empty;

        object message = null;

        while (!(x < 0)) //do this while not end of line (x = -1)
        {
            readStream.Position = endOffset;

            //line reader
            while (found == false)  //keep reading characters until end of line found
            {
                x = readStream.ReadByte();
                if (x < 0)
                {
                    found = true;
                    break;
                }
                // else if ((x == 10) || (x == 13))
                if ((x == 10) || (x == 13))
                {
                    ch = System.Convert.ToChar(x);
                    line = line + ch;
                    x = readStream.ReadByte();
                    if ((x == 10) || (x == 13))
                    {
                        ch = System.Convert.ToChar(x);
                        line = line + ch;
                        found = true;
                    }
                    else
                    {
                        if (x != 10 && (x != 13))
                        {
                            readStream.Position--;
                        }
                        found = true;
                    }
                }
                else
                {
                    ch = System.Convert.ToChar(x);
                    line = line + ch;
                }
            }//while - end line reader 



            //examine line (is it one of the supported types?)
            if (type1.IsMatch(line))
            {
                message = line;
                endOffset = readStream.Position;

                break;
            }
            else
            {
                endOffset = readStream.Position;
                found = false;
                line = string.Empty;
            }

        }//while not end of line


        return message;
    }

}

//PROJECT 2 - contains classes that define the types

//TYPE1

namespace MessageTypes.Type1
{
public sealed class Type1
{
    public List<Part> S2 { get; set; }

    public Type1(string s)
    {
        S2 = new List<Part>();
        string[] parts = s.Split(':');
        for (int i = 0; i < parts.Length; i++)
        {
            S2.Add(new Part(parts[i]));
        }
    }
}

public sealed class Part
{
    public string P { get; set; }

    public Part(string s)
    {
        P = s;
    }
}
}

//TYPE 2

namespace MessageTypes.Type2
{
public sealed class FullString
{
    public string FS { get; set; }

    public FullString(string s)
    {
        FS = s;
    }
}
}

//PROJECT 3

class DoSomethingToTypeObject{

//detect type and call appropriate function to process

} 

//PROJECT 4 -- MAIN PROJECT with GUI

    public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        if (tabControl1.SelectedIndex == 0) //Processing Mode 1
        {
            //load file list from main window - Mode1 tab
            IOHandler.Read read = new IOHandler.Read(new string[2] { @"C:\file1.txt", @"C:\file2.txt" });

            //read files
            foreach (string file in read.FileList)
            {

                //while not end of stream
                myobject = read.ProcessFile(file);

                DoSomethingtoTypeObject DS = new DoSomethingtoTypeObject(myobject);

                //write transoformed object
                write(myobject);
            }
        }
    }
}
like image 698
sjs Avatar asked Oct 17 '12 20:10

sjs


2 Answers

You should use an interface, then make all of your types implement the interface. After doing that, you should then change your Read class to operate on the interface NOT the individual classes.

That way you can add as many types as you want and not have to update the Read class.

like image 174
mattytommo Avatar answered Oct 21 '22 03:10

mattytommo


I hope I understand you correctly.

The class you create in the Type project represent some objects that have different behaviors but the same data members and you would like to be able to use these easily within your projects without the hassle of having to explicitly list these objects.

I would create some base interface that all my objects in the Types project would implement. I would then use a Factory Class that would use reflection to collect all objects that implement said interface.

public interface iFoo
{
    string FoundItem { get; set; }
    string Expression { get; }
    string Value { get; set; }
    void sharedFunctionName();
}

public static class FooFactory
{
    public static List<iFoo> GetTypeList()
    {
        List<iFoo> types = new List<iFoo>();
        types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies()
                       from t in assembly.GetTypes()
                       where t.IsClass && t.GetInterfaces().Contains(typeof(iFoo))
                       select Activator.CreateInstance(t) as iFoo);

        return types;
    }
}

Then your Reader would receive all necessary information for the supported types without you having to manually dictate it anymore.

Since I guess the value type would be different at some point, you could use a Generic Interface like this :

public interface iFoo
{
    string FoundItem { get; set; }
    string Expression { get; }
    void sharedFunctionName();
}

public interface iFoo<T> : iFoo
{
    T Value { get; set; }
}

public static class FooFactory
{
    public static List<iFoo> GetTypeList()
    {
        List<iFoo> types = new List<iFoo>();
        types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies()
                       from t in assembly.GetTypes()
                       where t.IsClass && t.GetInterfaces().Contains(typeof(iFoo))
                       select Activator.CreateInstance(t) as iFoo);

        return types;
    }
}

public class FooBar : iFoo<int>
{

}

In this example the base interface iFoo is kept to ease the discovery process. Using generic interface would allow to kept your code Type safe (as opposed to using a Value of type object) but you will have to add some logic when recovering your objects to be able to access your Value properly.

Plus, if you ever need to create functions that would require to be shared within all your objects you would be able to add extension methods within the Factory Class and Voilà.

EDIT:

Based on the new information:

Your Types correspond to a type of data that you will find in a file based on some regular expression. There might be different type of transformation based on the user selection and the Type.

We know that the user will have to pick a mode from a list and this will affect the transformation to apply on the Types.

So this is what I would do : I would move the transformation logic right into the Type class, polymophism will take care of exactly which transformation will be called.

I would put the RegularExpression to use to detect the Type into the Type itself, this will allow you to use reflection and the Factory class discuss earlier more easily.

This way, everything is standard. Your reader is aware of any new type you create in the type project without manual intervention and once detected the right transformation could be applied and the original string is always accessible.

public enum UserMode {Mode1, Mode2};

public interface iType
{
   string Expression {get;}
   string OriginalString {get; set;}
   string Transform(UserMode Mode);
   iType getNewInstance(string OriginalString);

}

public class Type1 : iType
{
   public string Expression {get { return "RegularExpression"; }}
   public string OriginalString {get; set;}
   //Add any other private members you need to accomplish your work here.
   public string Transform(UserMode Mode)
   {
      switch(Mode)
      {
         case UserMode.Mode1:
             //write the transformation code for this scenario
             return ResultString;
             break;
      }
   }

   public iType getNewInstance(string Original)
   {
     return (iType)(new Type1(){ OriginalString = Original });
   }
}

public static class TypeFactory
{
   public static List<iType> GetTypeList()
   {
       List<iType> types = new List<iType>();
       types.AddRange(from assembly in AppDomain.CurrentDomain.GetAssemblies()
                      from t in assembly.GetTypes()
                      where t.IsClass && t.GetInterfaces().Contains(typeof(iType))
                      select Activator.CreateInstance(t) as iType);

       return types;
    }
}

Now, all you will have to do if match the expression from the iTypes in the list. When you have a match you do :

var TransformationReady = from t in TypeFactory.GetTypeList()
                          where Regex.IsMatch(YourFileLine, t.Expression)
                          select t.getNewInstance(Regex.Match(YourFileLine, t.Expression));
like image 24
Yan Brunet Avatar answered Oct 21 '22 02:10

Yan Brunet