C# passing multiple properties as parameters for fluid logging
I am trying to pass multiple properties to a method for seamless logging. I am iterating over a list of files and hope to log them based on the properties' values. Currently, I am using enum
and bool
properties, but I can imagine needing to use other parameters in the future.
public enum MyTypes {
Type1,
Type2,
Type3
}
public class MyFile {
public string Filename { get; set; }
public bool ClassBool { get; set; }
public MyTypes Type { get; set; }
}
Without this optimization, the method would have to contain loops for each comparison:
public void LogFiles(List<MyFile> files) {
logger.Info("--- Begin Type1 Print ---\n\n");
for (int i = 0; i < files.Count; i++) {
if (files[i].Type == MyTypes.Type1)
logger.Info(files[i].Filename);
}
logger.Info("--- End Type1 Print ---\n\n");
logger.Info("--- Begin Type2 Print ---\n\n");
for (int i = 0; i < files.Count; i++) {
if (files[i].Type == MyTypes.Type2)
logger.Info(files[i].Filename);
}
logger.Info("--- End Type2 Print ---\n\n");
logger.Info("--- Begin Class Bool Print ---\n\n");
for (int i = 0; i < files.Count; i++) { //Example of not passing the same param
if (ClassBool == true)
logger.Info(files[i].Filename);
}
logger.Info("--- End Class Bool Print ---\n\n");
}
I would like to pass in the files, a logging message (that may not be the same as the name of the property), and the property. I imagine I will also have to pass in a delegate of some sort, but I don't know how to do that yet. I imagine something similar to this (not sure how to exactly code this, so it's just instructive pseudocode):
public void LogFiles(List<MyFile> files, string message, PropertyInfo prop, Delegate<UnsureHere> del)
logger.Info($"--- Begin {message} Print ---\n\n");
for (int i = 0; i < files.Count; i++) {
if (/*may use delegate here*/)
logger.Info(/*access property*/);
}
logger.Info($"--- End {message} Print ---\n\n");
}
All of this to achieve a use case like this:
List<MyFile> files = new();
LogFiles(files, "Type1", /*not sure how to pass these*/);
LogFiles(files, "Type2", /*not sure how to pass these*/);
LogFiles(files, "Class Bool", /*not sure how to pass these*/);
I understand that something similar could be achieved with a single loop and a switch statement, but I like the format that the code above would produce. Is this possible? Thank you for the help!
Solution 1:
You can add some dynamic function parameters to achieve your goal. You need two dynamic functions:
-
one to return a bool based on the contents of a MyFile which will control whether the information is logged
Func<MyFile, bool> includeSelector
-
another one to return the string data to log
Func<MyFile, string> dataSelector
You could implement the above like so:
public void LogFiles(
List<MyFile> files,
string message,
Func<MyFile, bool> includeSelector,
Func<MyFile, string> dataSelector
)
{
logger.Info($"--- Begin {message} Print ---\n\n");
for (int i = 0; i < files.Count; i++)
{
if (includeSelector(files[i]))
logger.Info(dataSelector(files[i]));
}
logger.Info($"--- End {message} Print ---\n\n");
}
Using LINQ, you could shorten it to:
public void LogFiles(
List<MyFile> files,
string message,
Func<MyFile, bool> includeSelector,
Func<MyFile, string> dataSelector
)
{
logger.Info($"--- Begin {message} Print ---\n\n");
foreach (var file in files.Where(f => includeSelector(f)))
logger.Info(dataSelector(f);
logger.Info($"--- End {message} Print ---\n\n");
}
You would call this using
LogFiles(files, "Type1", x => x.ClassBool, x => x.Filename);
x => x.ClassBool
is a function that will take a MyFile argument on the left side of the arrow and return the result of the operation on the right side. Note that there is no actual variable x declared in your code; I just chose to use that as the variable name for that dynamic function.
This is the same idea as a LINQ expression.