• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece!🔗 Click here to enter!

C# Dynamic Function calling

Status
Not open for further replies.
Level 29
Joined
Oct 24, 2012
Messages
6,543
Hello everyone. I was making an Exception handling system that catches and handles different exceptions through calling functions that are stored in a dictionary based on the type.
I wanted to know if there are any downsides to the way I have done things and if there are any ways to improve it. ( not all exceptions are being handled yet, it is only a mock-up).
If there are any optimizations I can make it is appreciated.

Thanks for any help.

JASS:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExceptionMonitoring
{

    class Exceptions
    {
        private string StrExceptionsFilePath { get; set; }
        public bool BoolRegisterNewExceptions { get; set; }
        private Dictionary<Type, ExceptionDelegate> DictTypeDelegate = new Dictionary<Type, ExceptionDelegate>();
        private Dictionary<Type, string> DictFilePaths = new Dictionary<Type, string>();

        private delegate string[] ExceptionDelegate(Exception ex);

        public Exceptions()
        {
            BoolRegisterNewExceptions = true;
            FileInfo path = new FileInfo("Exceptions.txt");
            StrExceptionsFilePath = path.FullName;
            DictTypeDelegate.Add(typeof(FileNotFoundException), StandardExc);
            //DictFilePaths.Add(typeof(FileNotFoundException), StrExceptionsFilePath);  //  Not needed unless file paths are different.
        }

        private string[] StandardExc(Exception ex)
        {
            return new string[] { "Date and Time of Error is " + DateTime.Now.ToString(), 
                "The type of Exception is " + ex.GetType().ToString(), ex.Message,
                "Stack trace = " + ex.StackTrace, "   " };
        }

        public void SaveException(Exception ex)
        {
            try
            {
                Type t = ex.GetType();
                if (DictTypeDelegate.ContainsKey(t))
                {
                    SaveError((DictTypeDelegate[t](ex)), (DictFilePaths.ContainsKey(t)) ? DictFilePaths[t] : StrExceptionsFilePath);
                }
                else if (BoolRegisterNewExceptions)
                {
                    DictTypeDelegate.Add(t, StandardExc);
                    SaveException(ex);
                }
            }
            catch (Exception exc)
            {
            }
        }

        public static void SaveError(string[] str, string filePath)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
            using (StreamWriter sw = new StreamWriter(fs))
            {
                foreach (string s in str)
                {
                    sw.WriteLine(s);
                }
            }
        }
    }
}

JASS:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExceptionMonitoring
{

    class Exceptions
    {
        private string StrExceptionsFilePath { get; set; }
        private Dictionary<Type, ExceptionDelegate> DictTypeDelegate = new Dictionary<Type, ExceptionDelegate>();
        private Dictionary<Type, string> DictFilePaths = new Dictionary<Type, string>();

        public Exceptions()
        {
            FileInfo path = new FileInfo("Exceptions.txt");
            StrExceptionsFilePath = path.FullName;
            DictTypeDelegate.Add(typeof(FileNotFoundException), StandardExc);
            DictTypeDelegate.Add(typeof(DirectoryNotFoundException), StandardExc);
            //DictTypesFuncts.Add(typeof(System.Reflection.TargetParameterCountException), new Func<Exception, string[]>(StandardExc));
            //DictFilePaths.Add(typeof(FileNotFoundException), StrExceptionsFilePath);  //  Not needed unless file paths are different.
        }

        public delegate string[] ExceptionDelegate(Exception ex);

        public string[] StandardExc(Exception ex)
        {
            return new string[] { "Date and Time of Error is " + DateTime.Now.ToString(), 
                "The type of Exception is " + ex.GetType().ToString(), ex.Message,
                "Stack trace = " + ex.StackTrace, "   " };
        }

        public void SaveException(Exception ex)
        {
            try
            {
                Type t = ex.GetType();
                if (DictTypeDelegate.ContainsKey(t))
                {
                    string path;
                    if (DictFilePaths.ContainsKey(t))
                    {
                        path = DictFilePaths[t];
                    }
                    else
                    {
                        path = StrExceptionsFilePath;
                    }
                    SaveError(((string[])DictTypeDelegate[t](ex)), path);
                }
                else
                {
                    MessageBox.Show("The type " + t.ToString() + " has not been added yet.");
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show("Error Recieved = " + exc.GetType().ToString() + " " + exc.Message);
            }
        }

        public static void SaveError(string[] str, string filePath)
        {
            File.WriteAllLines(filePath, File.ReadAllLines(filePath).Concat(str));
        }
    }
}
JASS:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExceptionMonitoring
{

    class Exceptions
    {
        private string StrExceptionsFilePath { get; set; }
        private Dictionary<Type, Delegate> DictTypesFuncts = new Dictionary<Type, Delegate>();
        private Dictionary<Type, string> DictFilePaths = new Dictionary<Type, string>();

        public Exceptions()
        {
            FileInfo path = new FileInfo("Exceptions.txt");
            StrExceptionsFilePath = path.FullName;
            DictTypesFuncts.Add(typeof(FileNotFoundException), new Func<Exception, string[]>(StandardExc));
            //DictFilePaths.Add(typeof(FileNotFoundException), StrExceptionsFilePath);  //  Not needed unless file paths are different.
        }

        public string[] StandardExc(Exception ex)
        {
            return new[] { "Date and Time of Error is " + DateTime.Now.ToString(), 
                "The type of Exception is " + ex.GetType().ToString(), ex.Message, 
                "The error is in " + ex.TargetSite.ToString(), "   " };
        }

        public void SaveException(Exception ex)
        {
            Type t = ex.GetType();
            if (DictTypesFuncts.ContainsKey(t))
            {
                string path;
                if (DictFilePaths.ContainsKey(t))
                {
                    path = DictFilePaths[t];
                }
                else
                {
                    path = StrExceptionsFilePath;
                }
                SaveError((string[])DictTypesFuncts[t].DynamicInvoke(ex), path);
            }
            else
            {
                MessageBox.Show("The type " + t.ToString() + " has not been added yet.");
            }
        }

        public static void SaveError(string[] str, string filePath)
        {
            File.WriteAllLines(filePath, File.ReadAllLines(filePath).Concat(str));
        }
    }
}
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
If C# is anything like Java then there is a slight performance penalty to dynamic method invocation since the virtual machine JIT compiler cannot optimize them very well. This is why it may be recommended to use subclass instances rather since the JIT compiler can optimize those better.

Do you really need to handle exceptions like this? If exceptions are anything like Java then they have inheritance which you can use to handle them where appropriate. It is also inefficient to handle exceptions on a regular basis, you should rather make the code avoid exception generation unless necessary.
 

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
I think DynamicInvoke is not needed here. You should define your own delegate type and just call the delegate without DynamicInvoke.

C#:
// declare delegate type:
public delegate string[] ExceptionHandler(Exception e);

// the dictionary: 
Dictionary<Type, ExceptionHandler> DictTypesFuncts = new Dictionary<Type, ExceptionHandler>();

// add delegate to dictionary:
DictTypesFuncts.Add(typeof(FileNotFoundException), StandardExc);

// or define the delegate inline:
DictTypesFuncts.Add(typeof(FileNotFoundException), delegate(Exception ex) { 
	return new[] { "bla" , "blub"};
});

// calling the delegate:
string[] msgs = DictTypesFuncts[err.GetType()](err);


I am not sure if it is a good idea to handle exceptions like this. Maybe you should look into a standard logging framework.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
I am making the code as good as I can to remove the need for Exceptions but I wanted this as a log for the ones that you can't stop. Like if you are copying a file and its path gets removed during the process ( example is on external drive). It then calls the function.
I was also using this to learn more about dynamic calling and when it is and is not useful. I wanted to be able to call them so I didn't need a huge ITE to check and handle the different exceptions.

@peg
I'm not really sure what your example does as it seems to store the Exception into the delegate string[] variable which means that it will only run that instance of the exception error. Can you explain a little more about how yours works. I just learned delegates then I threw this together so I'm not to sure about them that much.

@ DSG and peg
Thanks for your answers and help. +rep (if i can)
Any more info is appreciated.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
why not use Try,Catch?

I do use try catch. I then pass the caught Exception to this. It then loads the function to be called by checking which Exception type it is. Then it runs the function based on the type of Exception.

The main reason I did it this way was mainly to learn different techniques for programming. It also makes everything smaller. So instead of using ITES to check what exception it is it will load it from the dictionary.

Edit: Changed the above to this. If anyone could help me optimize it a little more and if you have any info on downsides to this way it would help a lot thanks.

JASS:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExceptionMonitoring
{

    class Exceptions
    {
        private string StrExceptionsFilePath { get; set; }
        private Dictionary<Type, ExceptionDelegate> DictTypeDelegate = new Dictionary<Type, ExceptionDelegate>();
        private Dictionary<Type, string> DictFilePaths = new Dictionary<Type, string>();

        public Exceptions()
        {
            FileInfo path = new FileInfo("Exceptions.txt");
            StrExceptionsFilePath = path.FullName;
            DictTypeDelegate.Add(typeof(FileNotFoundException), StandardExc);
            DictTypeDelegate.Add(typeof(DirectoryNotFoundException), StandardExc);
            //DictTypesFuncts.Add(typeof(System.Reflection.TargetParameterCountException), new Func<Exception, string[]>(StandardExc));
            //DictFilePaths.Add(typeof(FileNotFoundException), StrExceptionsFilePath);  //  Not needed unless file paths are different.
        }

        public delegate string[] ExceptionDelegate(Exception ex);

        public string[] StandardExc(Exception ex)
        {
            return new string[] { "Date and Time of Error is " + DateTime.Now.ToString(), 
                "The type of Exception is " + ex.GetType().ToString(), ex.Message,
                "Stack trace = " + ex.StackTrace, "   " };
        }

        public void SaveException(Exception ex)
        {
            try
            {
                Type t = ex.GetType();
                if (DictTypeDelegate.ContainsKey(t))
                {
                    string path;
                    if (DictFilePaths.ContainsKey(t))
                    {
                        path = DictFilePaths[t];
                    }
                    else
                    {
                        path = StrExceptionsFilePath;
                    }
                    SaveError(((string[])DictTypeDelegate[t](ex)), path);
                }
                else
                {
                    MessageBox.Show("The type " + t.ToString() + " has not been added yet.");
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show("Error Recieved = " + exc.GetType().ToString() + " " + exc.Message);
            }
        }

        public static void SaveError(string[] str, string filePath)
        {
            File.WriteAllLines(filePath, File.ReadAllLines(filePath).Concat(str));
        }
    }
}
 
Last edited:

peq

peq

Level 6
Joined
May 13, 2007
Messages
171
The SaveException still looks a little bit ugly, but I think I cannot help much, as I don't know the C# API very well. Here is what I think looks as if it could be improved:
  1. In SaveException: Having 9 lines for getting a value from a dictionary with a default if it does not exist seems a bit too long. If there is nothing for this in the standard API you might want to define your own extension method as suggested here.
  2. The cast should not be needed in SaveError(((string[])DictTypeDelegate[t](ex)), path);
  3. In SaveError: That is not very efficient for appending a string to a file and I think it is not save. If the writer crashes you could loose older log entries. Try to use something like File.AppendText. If there are a lot of log entries there are even more clever ways to do the logging efficiently.


PS: downside of your approach is mainly that it is more complicated than it has to be. Usually the toplevel try-catch should be called very rarely and only for really severe exceptions like out-of-memory or for programming errors like index-out-of-bound. For me it was usually enough to distinguish 2-3 different cases. Mostly it's just showing the user some error message and logging the exception, as there is not much which can be done in the case of a severe error. Non severe exceptions like FileNotFound should be handled more closely to the source of the exception anyhow, at a place where you can react to the exception directly.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
The SaveException still looks a little bit ugly, but I think I cannot help much, as I don't know the C# API very well. Here is what I think looks as if it could be improved:
  1. In SaveException: Having 9 lines for getting a value from a dictionary with a default if it does not exist seems a bit too long. If there is nothing for this in the standard API you might want to define your own extension method as suggested here.
  2. The cast should not be needed in SaveError(((string[])DictTypeDelegate[t](ex)), path);
  3. In SaveError: That is not very efficient for appending a string to a file and I think it is not save. If the writer crashes you could loose older log entries. Try to use something like File.AppendText. If there are a lot of log entries there are even more clever ways to do the logging efficiently.


PS: downside of your approach is mainly that it is more complicated than it has to be. Usually the toplevel try-catch should be called very rarely and only for really severe exceptions like out-of-memory or for programming errors like index-out-of-bound. For me it was usually enough to distinguish 2-3 different cases. Mostly it's just showing the user some error message and logging the exception, as there is not much which can be done in the case of a severe error. Non severe exceptions like FileNotFound should be handled more closely to the source of the exception anyhow, at a place where you can react to the exception directly.

1) That was changed to a one liner using the shorthand operator.
2) I removed that when I changed the function delegate in the dictionary. Forgot to update that here.
3) I changed that recently to a Filestream.

This is mainly for debugging purposes. The errors show me where I need to improve my code.

Thanks for your help on this. +rep if i can.

There are a few things i changed and forgot to post the updated one.

JASS:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ExceptionMonitoring
{

    class Exceptions
    {
        private string StrExceptionsFilePath { get; set; }
        public bool BoolRegisterNewExceptions { get; set; }
        private Dictionary<Type, ExceptionDelegate> DictTypeDelegate = new Dictionary<Type, ExceptionDelegate>();
        private Dictionary<Type, string> DictFilePaths = new Dictionary<Type, string>();

        private delegate string[] ExceptionDelegate(Exception ex);

        public Exceptions()
        {
            BoolRegisterNewExceptions = true;
            FileInfo path = new FileInfo("Exceptions.txt");
            StrExceptionsFilePath = path.FullName;
            DictTypeDelegate.Add(typeof(FileNotFoundException), StandardExc);
            //DictFilePaths.Add(typeof(FileNotFoundException), StrExceptionsFilePath);  //  Not needed unless file paths are different.
        }

        private string[] StandardExc(Exception ex)
        {
            return new string[] { "Date and Time of Error is " + DateTime.Now.ToString(), 
                "The type of Exception is " + ex.GetType().ToString(), ex.Message,
                "Stack trace = " + ex.StackTrace, "   " };
        }

        public void SaveException(Exception ex)
        {
            try
            {
                Type t = ex.GetType();
                if (DictTypeDelegate.ContainsKey(t))
                {
                    //string path = (DictFilePaths.ContainsKey(t)) ? DictFilePaths[t] : StrExceptionsFilePath;
                    SaveError((DictTypeDelegate[t](ex)), (DictFilePaths.ContainsKey(t)) ? DictFilePaths[t] : StrExceptionsFilePath);
                }
                else if (BoolRegisterNewExceptions)
                {
                    DictTypeDelegate.Add(t, StandardExc);
                    SaveException(ex);
                }
            }
            catch (Exception exc)
            {
            }
        }

        public static void SaveError(string[] str, string filePath)
        {
            using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
            using (StreamWriter sw = new StreamWriter(fs))
            {
                foreach (string s in str)
                {
                    sw.WriteLine(s);
                }
            }
        }
    }
}
 
Status
Not open for further replies.
Top