I am trying to invoke a function for every member of a list, but pass additional parameters to the delegate.
if I have a list called documents
List<string> documents = GetAllDocuments();
Now I need to iterate over the documents and call a method for every entry. I can do it using something like
documents.ForEach(CallAnotherFunction);
This would require the CallAnotherFunction to have a definition like
public void CallAnotherFunction(string value)
{
//do something
}
However, I need another parameter in CallAnotherFunction called, say content, that is dependent on the calling list.
So, my ideal definition would be
public void CallAnotherFunction(string value, string content)
{
//do something
}
And I would like to pass content as part of the ForEach call
List<string> documents = GetAllDocuments();
documents.ForEach(CallAnotherFunction <<pass content>>);
List<string> templates = GetAllTemplates();
templates.ForEach(CallAnotherFunction <<pass another content>>);
Is there a way I can achieve this without having to define different functions, or use iterators?
Use lambda expressions instead of method groups:
List<string> documents = GetAllDocuments();
documents.ForEach( d => CallAnotherFunction(d, "some content") );
List<string> templates = GetAllTemplates();
templates.ForEach( t => CallAnotherFunction(t, "other content") );
Use lambda expression:
string content = "Other parameter value";
documents.ForEach(x => CallAnotherFunction(x, content));
Related
Is it possible to complete this method? Is it possible in the latest version of C#? Thinking about this as a DSL to configure a system for watching for certain property changes on certain objects.
List<string> list = GetProps<AccountOwner>(x => new object[] {x.AccountOwnerName, x.AccountOwnerNumber});
// would return "AccountOwnerName" and "AccountOwnerNumber"
public List<string> GetProps<T>(Expression<Func<T, object[]>> exp)
{
// code here
}
In C# 6, you'd use:
List<string> list = new List<string>
{
nameof(AccountOwner.AccountOwnerName),
nameof(AccountOwner.AccountOwnerNumber)
};
Before that, you could certainly break the expression tree apart - the easiest way of working out how is probably to either use an expression tree visualizer, or use the code you've got and put a break point in the method (just make it return null for now) and examine the expression tree in the debugger. I'm sure it won't be very complicated - just a bit more than normal due to the array.
You could possibly simplify it using an anonymous type, if you use:
List<string> list = Properties<AccountOwner>.GetNames(x => new {x.AccountOwnerName, x.AccountOwnerNumber});
Then you could have:
public static class Properties<TSource>
{
public static List<string> GetNames<TResult>(Func<TSource, TResult> ignored)
{
// Use normal reflection to get the properties
}
}
If you don't care about the ordering, you could just use
return typeof(TResult).GetProperties().Select(p => p.Name).ToList();
If you do care about the ordering, you'd need to look at the names the C# compiler gives to the constructor parameters instead - it's a bit ugly. Note that we don't need an expression tree though - we only need the property names from the anonymous type. (An expression tree would work just as well, admittedly.)
Without c# 6 and nameof, you could get a property name from a expression tree like:
using System.Linq.Expressions;
//...
static string GetNameOf<T>(Expression<Func<T>> property)
{
return (property.Body as MemberExpression).Member.Name;
}
Using it like:
GetNameOf(() => myObject.Property);
Not directly usable for an array of objects, but you could make an overload to take an array of expressions... something like:
static string[] GetNameOf(IEnumerable<Expression<Func<object>>> properties)
{
return properties.Select(GetNameOf).ToArray();
}
And use it like
GetNameOf(
new Expression<Func<object>>[]
{
() => x.AccountOwnerName,
() => x.AccountOwnerNumber
}
);
Demonstrating fiddle: https://dotnetfiddle.net/GsV96t
Update
If you go this route, the original GetNameOf for a single property won't work for value types (since they get boxed to object in the Expression and now the expression uses Convert internally). This is easily solvable by changing the code to something like:
static string GetNameOf<T>(Expression<Func<T>> property)
{
var unary = property.Body as UnaryExpression;
if (unary != null)
return (unary.Operand as MemberExpression).Member.Name;
return (property.Body as MemberExpression).Member.Name;
}
Updated fiddle: https://dotnetfiddle.net/ToXRuu
Note: in this updated fiddle I've also updated the overloaded method to return a List instead of an array, since that's what was on your original code
May I how to save all list of guid's in List object, without using foreach and wanted to send all guid's to db method
var folderRequestIDs = from GridViewRow msgRow in gvwSearchResult.Rows
where ((CheckBox)msgRow.FindControl("chkSNO")).Checked
select ((Label)msgRow.FindControl("lblFolderRequestId")).Text;
foreach (string Id in folderRequestIDs)
{
dataSave(Id, "New")
}
You may use string.join to join all id in the list with "," or any other symbol you want.
var idList = string.Join(",", folderRequestIDs);//you will get a string "id1,id2,id3..."
As I see it, dataSave method accepts as an argument only an ID. Right? Its declaration should look something like this
void dataSave(int id){
...
}
If you want to give a collection as an argument to the specific function, then you would have to either alter it accepting a collection as an argument, or you could override it using the override keyword... This means that the function declaration should look something like this...
void dataSave(List<int> idList){
...
}
Hope I helped!
just I want to ask is it possible to Call function as ToUpper or ToLower() as string for example f.Name.Replace(" ", "_").ToLower()".ToLower()" or "ToUpper" for example if user select convert file name to ToLower() from dropdown list.it will take the value 'ToLower' and added to in of the function and executed the function call
Well, not cleanly. You could use reflection to find the method but you'd have to know the exact type that the function will be applied to since you won't get sophisticated binding like you do at compile-time. Plus you'll be limited to one method signature (in this case, no input parameters and returns a string).
To support any string function with that signature you could use:
string val = f.Name.Replace(" ", "_").ToLower();
string strCommand = "ToUpper";
MethodInfo theMethod = typeof(string).GetMethod(strCommand,new Type[] {});
string result = (string)(theMethod.Invoke(val,null));
But a simple switch seems cleaner and safer here.
The conditional operator (?:) would probably be a better option here
var fileName = f.Name.Replace(" ", "_");
fileName = ddl.SelectedValue == "ToLower" ? fileName.ToLower() : fileName.ToUpper();
Using Reflection for something as trivial as this is pretty unnecessary.
If you know the methods you want to call before, you can register them in a dictionary (key = name of the function or something user friendly, value = function that performs the conversion):
var dict = new Dictionary<string, Func<string, string>>();
dict.Add("Lowercase", x => x.ToLower());
dict.Add("Uppercase", x => x.ToUpper());
You can bind the drop down list to the dictionary (text = Key, value = Value) and perform the conversion as follows (ddl is the name of the DropDownList):
var f = (Func<string, string>)ddl.SelectedValue;
var convStr = f(inputStr);
You can create a defined dictionary of functions, which you can access by name of the action. You will get rid of any conditional statements and of all reflection. Example is given below:
var actions = new Dictionary<string, Func<string, string>>()
{
//keys may have other, human-readable values
{"ToLower", s => s.ToLower()},
{"ToUpper", s => s.ToUpper()}
};
//you will receive this from drop down list, it's the name of the action
var userSelectedOption = "ToUpper";
//string that you want to process
var myString = "some other user input";
//selecting appropriate action and passing myString to it
var transformedString = actions[userSelectedOption](myString);
//prints "SOME OTHER USER INPUT"
Console.WriteLine(transformedString);
Using reflection, you can call a methods using a string:
String ToLower = "ToLower";
String SometString = "This Is My String";
Type stringType = typeof(String);
// Get the overload of ToLower that takes no parameters
MethodInfo method = stringType.GetMethod(ToLower, System.Type.EmptyTypes);
// Invoke the method with no parameters
String newString = (String)method.Invoke(SometString, null);
MessageBox.Show(newString);
If I have understood your question correctly, you want to call a method based on a string.
You can use reflection, which is a topic in and of itself, but to get you started you can do something like:
MethodInfo chosenMethod = typeof(string).GetMethod(methodName, Types.EmptyTypes);
chosenMethod.Invoke(stringToModify, new object[]{});
But if it is a very limited set of functions you want to expose, you should just do a switch statement over the user input:
switch(methodName)
{
case "ToLower":
stringToModify.ToLower();
break;
...
You can use a simple switch statement:
switch (value) // Assuming value is of type SomeEnum
{
case SomeEnum.ToLower:
return f.ToLower()
case SomeEnum.ToUpper:
return f.ToUpper();
default:
//Do the Default
break;
}
This is how you parse what you get from the UI:
public static T ParseEnum<T>( string value )
{
return (T) Enum.Parse( typeof( T ), value, true );
}
Perhaps you might need to avoid limitations is sort of a Runtime intepreter of C#, like eval() function in JavaScript, which is not easily nor trivial to implement, but there is a good library out there called C# Eval ( http://csharp-eval.com/Download.php ) that uses Reflection internally of course. It is heavier but more effective for this, or even for much more complex escenarios.
You might use it in this way for example:
class Person
{
public string Name;
public string Process(string expression)
{
return CsEval.Eval(this, expression);
}
}
And then might call it:
var f = new Person();
f.Name = "John";
f.Name = f.Process("Name.Replace(" ", "_").ToLower()");
Of course this is just an example of how it evaluate what is in the string, as you can see you just need to pass a "context" where the expression is going to mean something, which in this case is the current class ("this").
I'd like to write one method to accomplish a task rather than many. I would be passing lists whose types could be one of several custom classes. Is that possible? A sample to explain what I am after with "??" marking where I don't know how the syntax might work follows. Thank you.
private static void MoveSuccessRecords(List<??> thisList, string VUEFileName, string folderArchive, string folderError)
{
StreamWriter successWriter = new StreamWriter(folderArchive + VUEFileName.Replace(extension, "_COMPLETE" + extension), true);
CsvWriter successCSV = new CsvWriter(successWriter);
successCSV.WriteHeader<??>();
foreach (var item in thisList)
{
successCSV.WriteRecord(item);
}
successCSV.Dispose();
successWriter.Dispose();
successWriter.Close();
}
I believe you are looking to make the method generic. In which case you merely need to adapt the method signature so that it accepts a type argument T
static void MoveSuccessRecords<T>(List<T> thisList, string ..)
You can then choose to be explicit about the type argument or let the compiler deduce the generic type argument from the given argument
var someList = new List<string>();
MoveSuccessRecords<string>(someList);
You should make this method a generic method, in the same way that List is a generic class. You can reuse the generic parameter when giving the parameter for List:
private static void MoveSuccessRecords<T>(List<T> thisList, string VUEFileName, string folderArchive, string folderError)
{
StreamWriter successWriter = new StreamWriter(folderArchive + VUEFileName.Replace(extension, "_COMPLETE" + extension), true);
CsvWriter successCSV = new CsvWriter(successWriter);
successCSV.WriteHeader<T>();
foreach (var item in thisList)
{
successCSV.WriteRecord(item);
}
successCSV.Dispose();
successWriter.Dispose();
successWriter.Close();
}
Ok, without knowing all the details or pondering it too much, here is what I would do:
1) Use List to define thisList
2) Use item.GetType() to determine the type of item in the list (you indicated that while the objects might be similar, they might not be the same). An Interface would make this easier - but I didn't see a response to that comment. Then you can pass the appropriate item to the WriteRecord method.
Hope you get the answer you are looking for.
I'm looking for a way to pass in a list of strongly typed property names into a method that I can then dissect and get the properties that the caller is interested in. The reason I want to do this is to have a copy method that only copies the fields the user specifies. Right now, the method takes a list of strings to use with the Getvalues and get properties methods in reflection, but I want to guard against refactoring of properties and the strings not being updated by the developer.
I found this article Here, but unfortunately, it does not do a list. I can do something like:
public static void Copy(Expression<Func<TObject>> propertiesToCopy )
{
}
And then have the caller do
PropertyCopier<List<string>>.Copy(() => data);
But then I have to specify how many properties the caller can have like this:
public static void Copy(Expression<Func<TObject>> propertiesToCopy,Expression<Func<TObject>> propertiesToCopy2, Expression<Func<TObject>> propertiesToCopy3 )
{
}
This would allow for three properties. Is there anyway to add it to a List or Queryable<> to allow as many properties as the caller wants? I tried using the Add in List and having the Expression
Thanks in advance
Edit: I did find a few articles this evening that refer to using the C# param keyword to accomplish this. Are there any better or more efficient ways, or is this the best way to do it?
Use the params keyword to define a method that takes a variable number of arguments:
public static void PrintPropertyNames<T>(params Expression<Func<T, object>>[] properties)
{
foreach (var p in properties)
{
var expression = (MemberExpression)((UnaryExpression)p.Body).Operand;
string memberName = expression.Member.Name;
Console.WriteLine(memberName);
}
}
For instance, you could call the PrintPropertyNames method passing two expressions:
PrintPropertyNames<FileInfo>(f => f.Attributes, f => f.CreationTime);
This example displays the following output to the console:
Attributes
CreationTime