I have two objects of the same class type and I want to fetch the different valued columns with values from both the objects.
I have tried the below code but I'm able to fetch only the column names.
public List<string> GetChangedProperties<T>(T a, T b) where T : class
{
if (a != null && b != null)
{
if (object.Equals(a, b))
{
return new List<string>();
}
var allProperties = a.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
return allProperties.Where(p => !object.Equals(p.GetValue(a), p.GetValue(b))).Select(p => p.Name).ToList();
}
else
{
var aText = $"{(a == null ? ("\"" + nameof(a) + "\"" + " was null") : "")}";
var bText = $"{(b == null ? ("\"" + nameof(b) + "\"" + " was null") : "")}";
var bothNull = !string.IsNullOrEmpty(aText) && !string.IsNullOrEmpty(bText);
throw new ArgumentNullException(aText + (bothNull ? ", " : "") + bText);
}
}
You can use a Dictionary instead of a List:
public class Program
{
static public Dictionary<string, Tuple<object, object>> GetChangedProperties<T>(T a, T b) where T : class
{
if ( a != null && b != null )
{
if ( Object.Equals(a, b) )
{
return new Dictionary<string, Tuple<object, object>>();
}
var allProperties = a.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var result = new Dictionary<string, Tuple<object, object>>();
foreach ( var p in allProperties )
{
var v1 = p.GetValue(a);
var v2 = p.GetValue(b);
if ( !Object.Equals(v1, v2) )
result.Add(p.Name, new Tuple<object, object>(v1, v2));
}
return result;
}
else
{
var aText = $"{( a == null ? ( "\"" + nameof(a) + "\"" + " was null" ) : "" )}";
var bText = $"{( b == null ? ( "\"" + nameof(b) + "\"" + " was null" ) : "" )}";
var bothNull = !string.IsNullOrEmpty(aText) && !string.IsNullOrEmpty(bText);
throw new ArgumentNullException(aText + ( bothNull ? ", " : "" ) + bText);
}
}
public class Test
{
public int A { get; set; }
public int B { get; set; }
}
static void Main(string[] args)
{
var v1 = new Test { A = 10, B = 20 };
var v2 = new Test { A = 5, B = 20 };
var list = GetChangedProperties(v1, v2);
foreach ( var item in list )
Console.WriteLine($"{item.Key}: {item.Value.Item1.ToString()} != {item.Value.Item2.ToString()}");
Console.ReadKey();
}
}
Related
This question already has answers here:
Is there a string math evaluator in .NET?
(18 answers)
Closed 2 years ago.
I have a situation where i need to parse multiple n number of related fields (Do not want to evaluate):
string exp1 = "10";
string exp2 = "20";
string exp3= "exp1 + exp2 + 30";
string exp4 = "exp5 - exp3";
string exp5 = "exp3 / 10";
Dictionary<string,string> expressions = new Dictionary<string,string>();
expressions.Add("exp1", exp1);
expressions.Add("exp2", exp2);
expressions.Add("exp3", exp3);
expressions.Add("exp4", exp3);
expressions.Add("exp5", exp5);
Now we want to loop through all the expression fields and parse the expression with the actual values (Bodmas should also be applied) .So, after parsing, We want below as output:
exp1 = "10";
exp2 = "20";
exp3= "10 + 20 + 30";
exp4 = "((10 + 20 + 30 )/10) - (10+ 20 + 30)";
exp5 = "(10 + 29 + 30)/ 10";
What would be the data structure I should use here to parse it using a generic way? Would it be Binary Tree, graphs, ExpressionTrees?
The Code
using System.Collections.Generic;
using Microsoft.VisualBasic; //the code was written in VB and converted. make appropriate changes if you don't want to use this namespace
class Exp
{
private Dictionary<string, string> AllExpressions = new Dictionary<string, string>();
public void Add(string name, string value)
{
AllExpressions.Add(name, value);
}
public string ValueOf(string name)
{
var parts = Strings.Split(AllExpressions[name]);
for (int i = 0; i <= parts.Length - 1; i++)
{
if (AllExpressions.ContainsKey(parts[i]))
{
var partVal = ValueOf(parts[i]);
parts[i] = Information.IsNumeric(partVal) ? partVal : $"({partVal})";
}
}
return Strings.Join(parts);
}
}
Usage
Exp myExp = new Exp();
myExp.Add("exp1", "10");
myExp.Add("exp2", "20");
myExp.Add("exp3", "exp1 + exp2 + 30");
myExp.Add("exp4", "exp5 - exp3");
myExp.Add("exp5", "exp3 / 10");
// Test to see if we can get correct values
Console.WriteLine(myExp.ValueOf("exp1"));
Console.WriteLine(myExp.ValueOf("exp2"));
Console.WriteLine(myExp.ValueOf("exp3"));
Console.WriteLine(myExp.ValueOf("exp4"));
Console.WriteLine(myExp.ValueOf("exp5"));
The Result
10
20
10 + 20 + 30
((10 + 20 + 30) / 10) - (10 + 20 + 30)
(10 + 20 + 30) / 10
String replacement is unavoidable, but we can decrease the number of loops and replace operations by having a class to represent our expressions. When we have our arithmetic expressions wrapped in a reference type, we can add features and use them later:
public class ArithmeticExpression
{
public string Value;
public bool IsLiteral;
public bool IsFinalized;
public string ArithmeticSafeValue { get { return IsLiteral ? Value : "(" + Value + ")"; } }
public ArithmeticExpression(string value)
{
Value = value;
int dummy;
IsFinalized = IsLiteral = int.TryParse(value, out dummy);
}
}
And here is the methods to evaluate the final versions of our expressions, changing them to literals:
private static void Resolve(Dictionary<string, ArithmeticExpression> expressions)
{
foreach (string expressionKey in expressions.Keys.ToArray())
{
Resolve(expressionKey, expressions);
}
}
private static void Resolve(string expressionKey, Dictionary<string, ArithmeticExpression> expressions)
{
ArithmeticExpression expression = expressions[expressionKey];
if (expression.IsFinalized) return;
List<string> containedExpressions = expressions.Keys.Where(x => expression.Value.Contains(x)).ToList();
if (containedExpressions.Count > 0)
{
containedExpressions.ForEach((x) => Resolve(x, expressions));
containedExpressions.ForEach((x) => expression.Value = expression.Value.Replace(x, expressions[x].ArithmeticSafeValue));
expression.IsFinalized = true;
}
}
And here is how to use it:
public static void Main()
{
string exp1 = "10";
string exp2 = "20";
string exp3 = "exp1 + exp2 + 30";
string exp4 = "exp5 - exp3";
string exp5 = "exp3 / 10";
Dictionary<string, ArithmeticExpression> expressions = new Dictionary<string, ArithmeticExpression>();
expressions.Add("exp1", new ArithmeticExpression(exp1));
expressions.Add("exp2", new ArithmeticExpression(exp2));
expressions.Add("exp3", new ArithmeticExpression(exp3));
expressions.Add("exp4", new ArithmeticExpression(exp4));
expressions.Add("exp5", new ArithmeticExpression(exp5));
Resolve(expressions);
expressions.Keys.ToList().ForEach
(
(x) => Console.WriteLine("{0}: {1}", x, expressions[x].Value)
);
}
Hope this helps.
Well, not the most elegant solution, but I think it works
string exp1 = "10";
string exp2 = "20";
string exp3 = "exp1 + exp2 + 30";
string exp4 = "exp5 - exp3";
string exp5 = "exp3 / 10";
var dic = new Dictionary<String, String>();
dic.Add("exp1", exp1);
dic.Add("exp2", exp2);
dic.Add("exp3", exp3);
dic.Add("exp4", exp4);
dic.Add("exp5", exp5);
for (int i = 0; i < dic.Count; i++)
{
var dicItem = dic.ElementAt(i);
var splitted = dicItem.Value.Split(' ');
var sb = new StringBuilder();
foreach (var splittedItem in splitted)
{
if(dic.ContainsKey(splittedItem))
{
sb.Append(dic[splittedItem]);
}
else
{
sb.Append(splittedItem);
}
sb.Append(" ");
}
var parsed = sb.ToString();
if (parsed.Contains("exp"))
i--; //go back and try to evaluate it again
dic.Remove(dicItem.Key);
dic.Add(dicItem.Key, sb.ToString());
}
and not to make it more readable you can use DynamicExpression
var p = Expression.Parameter(typeof(String));
foreach (var exp in dic.Values)
{
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, exp);
System.Diagnostics.Trace.WriteLine(e.Body);
}
OUTPUT
10
20
((10 + 20) + 30)
(((((10 + 20) + (30 / 10)) - 10) + 20) + 30)
((10 + 20) + (30 / 10))
This solution only replaces the variables:
var results = expressions.ToDictionary(x => x.Key, x => x.Value);
bool expanded;
do
{
expanded = false;
foreach (var key in expressions.Keys)
{
foreach (var kvpReplaceWith in expressions)
{
if (key == kvpReplaceWith.Key)
{
continue;
}
var lengthBefore = results[key].Length;
results[key] = results[key].Replace(kvpReplaceWith.Key, "(" + kvpReplaceWith.Value + ")");
var lengthAfter = results[key].Length;
expanded = expanded || lengthBefore != lengthAfter;
}
}
}
while (expanded);
I've been trying to filter products with Elasticsearch for a few hours, unfortunately to no avail.
I need to find products that belong to certain categories and at the same time have selected several brands and one size.
Help :(
json screen
querycontainer build method
private QueryContainer CreateOrQueryFromFilter(QueryContainer queryContainer, SortedSet<string> filter, string fieldName)
{
if (filter != null && filter.Count > 0)
{
foreach (var item in filter)
{
queryContainer |= new TermQuery()
{
Name = fieldName + "named_query",
Boost = 1.1,
Field = fieldName,
Value = item
};
}
}
return queryContainer;
}
and search method
public ResultModel SearchRequest(RequestModel r)
{
string key = string.Format("search-{0}-{1}", r.CacheKey + "-" + ProductCatalog.Model.Extension.StringHelper.UrlFriendly(r.SearchText), r.Prefix);
node = new Uri("http://xxxx:9200/");
settings = new ConnectionSettings(node);
settings.DisableDirectStreaming();
settings.DefaultIndex("products");
client = new ElasticClient(settings);
// return AppCache.Get(key, () =>
// {
DateTime start = DateTime.Now;
ResultModel result = new ResultModel(r.Canonical, r.RouteObject);
if (!string.IsNullOrEmpty(r.Prefix))
{
result.Prefix = r.Prefix;
}
QueryContainer c = new QueryContainer();
if (r.CategoryFilterChilds != null && r.CategoryFilterChilds.Count > 0)
{
var a1 = new SortedSet<string>(r.CategoryFilterChilds.Select(a => (string)a.ToString()));
c = CreateOrQueryFromFilter(c, a1, "categories");
}
else
{
if (r.CategoryFilterRoots != null && r.CategoryFilterRoots.Count > 0)
{
var a1 = new SortedSet<string>(r.CategoryFilterRoots.Select(a => (string)a.ToString()));
c = CreateOrQueryFromFilter(c, a1, "categories");
}
else
{
// null
}
}
var filters = new AggregationDictionary();
if (r.IsBrandFilter)
{
c = CreateOrQueryFromFilter(c, r.SelectedBrands, "brands");
}
if (r.IsColorFilter)
{
c = CreateOrQueryFromFilter(c, r.SelectedBrands, "colors");
}
int skip = (r.Page * r.PageSize) - r.PageSize;
ISearchRequest r2 = new SearchRequest("products");
r2.From = 1;
r2.Size = r.PageSize;
r2.Query = c;
string[] Fields = new[] { "brands", "shopId" };
AggregationBase aggregations = null;
foreach (string sField in Fields)
{
var termsAggregation = new TermsAggregation("agg_" + sField)
{
Field = sField,
Size = 120,
Order = new List<TermsOrder> { TermsOrder.TermDescending }
};
if (aggregations == null)
{
aggregations = termsAggregation;
}
else
{
aggregations &= termsAggregation;
}
}
r2.Aggregations = aggregations;
var c2 = client.Search<ShopProductElastic>(r2);
var ShopsBuf = (Nest.BucketAggregate)(c2.Aggregations["agg_brands"]);
var ShopsCount = ShopsBuf.Items.Count();
var results = c2;
result.BrandsRequest = new SortedDictionary<string, int>();
foreach (Nest.KeyedBucket<object> item in ShopsBuf.Items)
{
result.BrandsRequest.Add((string)item.Key, (int)(item.DocCount ?? 0));
}
result.CategorySelected = r.CategoryCurrent;
result.TotalCount = 10;
var costam = results.Documents.ToList();
var targetInstance = Mapper.Map<List<Products>>(costam);
result.Products = targetInstance;
result.Page = r.Page;
result.PageSize = r.PageSize;
result.IsBrandFilter = r.IsBrandFilter;
result.IsColorFilter = r.IsColorFilter;
result.IsPatternFilter = r.IsPatternFilter;
result.IsSeasonFilter = r.IsSeasonFilter;
result.IsShopFilter = r.IsShopFilter;
result.IsSizeFilter = r.IsSizeFilter;
result.IsStyleFilter = r.IsStyleFilter;
result.IsTextileFilter = r.IsTextileFilter;
DateTime stop = DateTime.Now;
result.SearchTime = stop - start;
result.SearchText = r.SearchText;
return result;
// }, TimeSpan.FromHours(8));
}
I have all products that have a given brand or categories. However, i need all products of a selected brand in selected categories
I created a method that compares two lists' values and outputs the difference:
public static List<OT_Contact> Comparer(
List<OT_Contact> security_owner,
List<OT_Contact> borrower)
{
List<OT_Contact> nonborrowertrustor = security_owner.Except(borrower).ToList();
foreach (var nbt_list in nonborrowertrustor)
{
var list = nbt_list;
}
return nonborrowertrustor;
}
Can anyone tell me if there is something wrong with my code and how can I assign it to a variable, for example:
var result = Comparer(list1, list2);
Why are you not using Linq?
var trustys = security_owner
.Where(o => !borrower.Any(b=> b.Name == o.Name /* && compare more attributes from o and b */))
.ToList();
return trustys;
this will filter all items from security owner which aren't in borrower, using
(b=> b.Name == o.Name && .....)
as "equality measure".
Example:
using System.Collections.Generic;
using System.Linq;
internal class Program
{
public class OT_Contact
{
public int Age;
public string Email;
public string Name;
public OT_Contact(string name, string email, int age = int.MinValue)
{
Name = name;
Email = email;
Age = age;
}
public override string ToString()
{
return ($"{Name} {Email} " + (Age != int.MinValue ? Age.ToString() : "")).Trim();
}
}
static void Main(string[] args)
{
var owner = new List<OT_Contact>
{
new OT_Contact("paul","p#o", 5),
new OT_Contact("paul","pppp#o"),
new OT_Contact("mani","kkk"),
new OT_Contact("olaf", "olaf", 22)
};
var borr = new List<OT_Contact>
{
new OT_Contact("paul","popel#o", 5),
new OT_Contact("paul","pppp#o"),
new OT_Contact("mani","kkk",99),
new OT_Contact("olaf", "", 22)
};
var trust = owner.Where(o => !borr.Any(b => b.Name == o.Name && b.Email == o.Email)).ToList();
System.Console.WriteLine("Owner:\n " + string.Join("\n ", owner));
System.Console.WriteLine("\nBorrower:\n " + string.Join("\n ", borr));
System.Console.WriteLine("\nTrustys:\n " + string.Join("\n ", trust));
System.Console.ReadLine();
}
}
Output (Age is not important for equality, hence not inside the Any-Clause):
Owner:
paul p#o 5
paul pppp#o
mani kkk
olaf olaf 22
Borrower:
paul popel#o 5
paul pppp#o
mani kkk 99
olaf 22
Trustys:
paul p#o 5
olaf olaf 22
This example models the IEnumerable.Except(..) used in the question. It will find any owner that is not found in the borrower list. The euqality-condition only takes Name and Email under consideration. If it finds an borrower with the same Name and Email it will not output him in the resulting list.
Any borrower that is only in the owner list won't make it into to the result either - getting any borrower that is not in owner could be accomplished by simply swapping the lists around:
var onlyBorr = borr.Where(b => !owner.Any(o => o.Name == b.Name && o.Email == b.Email)).ToList();
A feasable implementation of an IEqualityComparer for this contrieved example could be
public class OT_ContactEqualityComparer : IEqualityComparer<OT_Contact>
{
public bool Equals(OT_Contact x, OT_Contact y)
{
if (x == null || y == null)
return false;
return (x.Name == y.Name && x.Email == y.Email);
}
public int GetHashCode(OT_Contact c)
=> (c.Name ?? "").GetHashCode() ^ (c.Email ?? "").GetHashCode();
}
which would allow the usage of IEnumerable.Except like this:
System.Console.WriteLine("\nIEquality:\n " + string.Join("\n ",
owner.Except(borr, new OT_ContactEqualityComparer())));
static class Example
{
public static string Method<T>(ref List<string> p2, out string p3, string p4)
{
...
}
public static string Method<T>(ref List<string> p2, out string p3, int p4)
{
...
}
}
The following obviously doesn't work, but that's the idea:
public static string MethodCaller(Type theType, ref List<string> p2, out string p3, string p4)
{
Method<theType>(ref p2, out p3, p4);
}
Using GetMethod? how does it know which of the two overloaded methods to use?
should we Use Expression.Call instead? how do we deal with the ref and out parameters?
Please help :)
This can be done through reflection, although finding the correct overload is a bit messy:
class Program
{
static void Main(string[] args)
{
List<string> p2 = new List<string>();
string p3;
string p4 = "input string";
string result = MethodCaller(typeof(DateTime), ref p2, out p3, p4);
}
public static string MethodCaller(Type theType, ref List<string> p2, out string p3, string p4)
{
MethodInfo method = (from m in typeof(Example).GetMethods()
let p = m.GetParameters()
where m.Name == "Method"
&& p.Length == 3
&& p[0].ParameterType.IsByRef
&& p[0].ParameterType.HasElementType
&& p[0].ParameterType.GetElementType() == typeof(List<string>)
&& p[1].ParameterType.IsByRef
&& p[1].ParameterType.HasElementType
&& p[1].ParameterType.GetElementType() == typeof(string)
&& p[2].ParameterType == typeof(string)
select m).Single();
MethodInfo genericMethod = method.MakeGenericMethod(theType);
object[] parameters = new object[] { null, null, p4 };
string returnValue = (string)genericMethod.Invoke(null, parameters);
p2 = (List<string>)parameters[0];
p3 = (string)parameters[1];
return returnValue;
}
}
static class Example
{
public static string Method<T>(ref List<string> p2, out string p3, string p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4);
p3 = "output string";
return "return value";
}
public static string Method<T>(ref List<string> p2, out string p3, int p4)
{
p2 = new List<string>();
p2.Add(typeof(T).FullName);
p2.Add(p4.ToString());
p3 = "output string";
return "return value";
}
}
I was looking for somehow similar approach, but unfortunately haven't found it - but then decided to solve it on my own. However - during prototyping I've discovered that 'out' and 'ref' are mutually exclusive - so you can support only one of them.
So I wanted to support syntax like:
object DoCall<A1>( String function, A1 a1 )
This will require to generate functions like:
object DoCall<A1>( String function, out A1 a1 )
object DoCall<A1>( String function, ref A1 a1 )
Which compiler will not like.
So I've decided to support only 'ref' keyword - since it can support in and out direction, but 'out' supports only out direction.
But additionally like someone might noticed - if you need to support all kind of parameters permutations - simple coding is not enough - you need to write code generator - what I did at the end.
So test code looks somehow like this:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace TestReflection
{
public class CustomClassAsArg
{
public string MyInfo { get; set; }
}
public class CallMe
{
public void Hello1(String msg, int i)
{
Console.WriteLine(msg + ": " + i.ToString());
}
public void Hello2(ref String msg)
{
msg += "out string";
}
public void Hello2(ref int a)
{
a += 2;
}
public string Hello3(string a)
{
return a + "--";
}
public static bool MyStaticMethod( int arg, ref String outs )
{
outs = "->" + arg.ToString();
return true;
}
public bool? ThreeStateTest( int i )
{
switch ( i )
{
case 0:
return null;
case 1:
return false;
case 2:
return true;
}
return null;
}
public void UpdateCC( CustomClassAsArg c )
{
c.MyInfo = "updated";
}
}
class Program
{
static void Main(string[] args)
{
ClassCaller.UpdateSourceCodeHelperFunctions(2);
CallMe m = new CallMe();
ClassCaller c = new ClassCaller(m);
string r = "in string ";
int arg = 1;
String sx = "";
object ox = c.DoCall("!MyStaticMethod", 23, ref sx);
Console.WriteLine(sx);
c.DoCall("Hello1", "hello world", 1);
c.DoCall("Hello2", ref r);
Console.WriteLine(r);
c.DoCall("Hello2", ref arg);
Console.WriteLine(arg.ToString());
bool? rt = (bool?)c.DoCall("ThreeStateTest", 0);
rt = (bool?)c.DoCall("ThreeStateTest", 1);
rt = (bool?)c.DoCall("ThreeStateTest", 2);
CustomClassAsArg ccarg = new CustomClassAsArg();
c.DoCall("UpdateCC",ccarg);
} //Main
}
}
And ClassCaller.cs itself:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
/// <summary>
/// Helper class for performing invoke function call in dynamically loaded assembly.
/// </summary>
public class ClassCaller
{
Type type;
object o;
bool throwOnError;
/// <summary>
/// Can specify class using only assembly name / class type without
/// actual class instance - if you intend to call only static methods.
/// </summary>
/// <param name="assemblyName">Assembly name</param>
/// <param name="className">Class name, including namespace</param>
/// <param name="_throwOnError">true if throw on error</param>
public ClassCaller(String assemblyName, String className, bool _throwOnError)
{
throwOnError = _throwOnError;
Assembly asm = AppDomain.CurrentDomain.GetAssemblies().Where(x => x.GetName().Name == assemblyName).FirstOrDefault();
if (asm == null)
{
if (_throwOnError)
throw new NullReferenceException("Assembly with name '" + assemblyName + "' was not found");
return;
}
type = asm.GetType(className, _throwOnError);
}
public ClassCaller(object _o)
{
type = _o.GetType();
o = _o;
}
/// <summary>
/// Gets method to invoke.
/// </summary>
/// <param name="func">Function name to get. Use '!' as a prefix if it's static function.</param>
/// <param name="types">Function argument types.</param>
/// <returns>Method to be invoked.</returns>
public MethodInfo GetFunc(String func, Type[] types)
{
bool bIsStatic = func.FirstOrDefault() == '!';
if (bIsStatic) func = func.Substring(1);
BindingFlags f = BindingFlags.Public | BindingFlags.NonPublic;
if (!bIsStatic)
f |= BindingFlags.Instance;
else
f |= BindingFlags.Static;
MethodInfo m = type.GetMethod(func, f, null, types, null);
if (m == null && throwOnError)
throw new NotSupportedException("Compatible function '" + func + "' not found");
return m;
}
//Autogenerated code starts (Do not edit)
public object DoCall(string func)
{
Type[] types = new Type[] { };
object[] args = new object[] { };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1>(string func, A1 a1)
{
Type[] types = new Type[] { typeof(A1) };
object[] args = new object[] { a1 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1>(string func, ref A1 a1)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType() };
object[] args = new object[] { a1 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
return r;
}
public object DoCall<A1, A2>(string func, A1 a1, A2 a2)
{
Type[] types = new Type[] { typeof(A1), typeof(A2) };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
return r;
}
public object DoCall<A1, A2>(string func, ref A1 a1, A2 a2)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType(), typeof(A2) };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
return r;
}
public object DoCall<A1, A2>(string func, A1 a1, ref A2 a2)
{
Type[] types = new Type[] { typeof(A1), typeof(A2).MakeByRefType() };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a2 = (A2)args[1];
return r;
}
public object DoCall<A1, A2>(string func, ref A1 a1, ref A2 a2)
{
Type[] types = new Type[] { typeof(A1).MakeByRefType(), typeof(A2).MakeByRefType() };
object[] args = new object[] { a1, a2 };
MethodInfo f = GetFunc(func, types);
if (f == null)
return null;
object r = f.Invoke(o, args);
a1 = (A1)args[0];
a2 = (A2)args[1];
return r;
}
//Autogenerated code ends
public static void UpdateSourceCodeHelperFunctions( int nParametersToSupport)
{
String srcFilename = new StackTrace(true).GetFrame(0).GetFileName();
String src = File.ReadAllText(srcFilename, Encoding.UTF8);
String autogenRegex = "(Autogenerated\\scode\\sstarts.*?[\r\n]{2})(.*)([\r\n]{2}\\s+//Autogenerated\\scode\\sends)";
if (!Regex.Match(src, autogenRegex, RegexOptions.Singleline).Success)
{
Console.WriteLine("Error: Invalid source code");
return;
}
string[] argType = new String[] { "", "ref" };
String s = "";
string lf = "\r\n";
string headSpace = " ";
for (int callArgs = 0; callArgs <= nParametersToSupport; callArgs++)
{
int[] argTypes = new int[callArgs];
int iterations = (int)Math.Pow(2, callArgs);
for (int i = 0; i < iterations; i++)
{
//public object DoCall<A1, A2>(String func, A1 a1, A2 a2)
s += headSpace;
s += "public object DoCall" + ((callArgs != 0) ? "<" : "");
s += String.Join(", ", Enumerable.Range(1, callArgs).Select(n => "A" + n));
s += (callArgs != 0) ? ">" : "";
s += "(string func";
String types = "";
String paramsList = "";
bool[] isRefType = new bool[callArgs];
for (int iArg = 0; iArg < callArgs; iArg++)
{
isRefType[iArg] = (((1 << iArg) & i) != 0);
String isRef = isRefType[iArg] ? "ref " : "";
String argTypeName = "A" + (iArg + 1);
String argName = "a" + (iArg + 1);
s += ", ";
s += isRef;
s += argTypeName + " " + argName;
if (iArg != 0)
{
types += ", ";
paramsList += ", ";
}
types += "typeof(" + argTypeName + ")";
if (isRefType[iArg])
types += ".MakeByRefType()";
paramsList += argName;
} //for
s += ")";
s += lf;
s += headSpace + "{" + lf;
//Type[] types = new Type[] { typeof(A1).MakeByRefType() };
s += headSpace + " ";
if( types.Length != 0 ) types += " ";
s += "Type[] types = new Type[] { " + types + "};";
s += lf;
//object[] args = new object[] { a1 };
s += headSpace + " ";
if( paramsList.Length != 0 ) paramsList += " ";
s += "object[] args = new object[] { " + paramsList + "};";
s += lf;
//MethodInfo f = GetFunc(func, types);
//if (f == null)
// return null;
//object r = f.Invoke(o, args);
s += headSpace + " MethodInfo f = GetFunc(func, types);" + lf;
s += headSpace + " if (f == null)" + lf;
s += headSpace + " return null;" + lf;
s += headSpace + " object r = f.Invoke(o, args);" + lf;
for (int iArg = 0; iArg < callArgs; iArg++)
{
if (!isRefType[iArg])
continue;
// a1 = (A1)args[0];
String argTypeName = "A" + (iArg + 1);
String argName = "a" + (iArg + 1);
s += headSpace + " ";
s += argName + " = (" + argTypeName + ")args[" + iArg + "];";
s += lf;
}
s += headSpace + " return r;" + lf;
s += headSpace + "}" + lf;
s += lf;
}
} //for
String oldautogenCode = Regex.Match(src, autogenRegex, RegexOptions.Singleline).Groups[2].Value;
//
// Visual studio text editor configuration affects spacing. We trim here everything so we can compare output.
//
oldautogenCode = oldautogenCode.Replace(" ", "").TrimStart('\r','\n');
String newautogenCode = s.Replace(" ", "").TrimStart('\r', '\n');
String newSrc = Regex.Replace(src, autogenRegex, "$1\r\n" + s + "$3", RegexOptions.Singleline);
if (oldautogenCode == newautogenCode)
{
Console.WriteLine("Source code is up-to-date.");
}
else
{
File.WriteAllText(srcFilename, newSrc, Encoding.UTF8);
}
} //UpdateSourceCodeHelperFunctions
} //class ClassCaller
so:
ClassCaller.UpdateSourceCodeHelperFunctions(2);
Function regenerates auto-generated code part to support - for demo purposes I'm supporting now only 2 parameters to be called, but typically you need more parameters - but this will increase autogenerated code size.
Auto-generated code can be updated only in debug mode, not in release configuration. (But this is not needed in release at all).
Maybe this is not direct answer to your question, but I think it follows your idea.
Reflect call requires all argument types to match 100% correctly - otherwise reflection will not be able to find required method.
Also this solution has limitation - for example it won't be able to find right method if some of parameters are optional - like:
void DoMethod( int a, int b = 0 );
So you can call:
DoMethod(5);
But not:
DoCall("DoMethod", 5);
it must be full set of arguments:
DoCall("DoMethod", 5, 0);
I understand that method calling through reflection might be expensive in consumed time wise, so think twice before using it.
Update 31.5.2016 I've also find out that C#'s 'dynamic' keyword can be used also for dynamic invocation of specific method without knowing reflection method call details, but dynamic operates only on instances, static method calls are still easier to make with ClassCaller.
I have a situation where my C# application with .Net 4 is accessing a static method within a static class making the method perform slower than when accessed by a single thread. Say for example, If the method is called by a single thread it takes around 1.5 minutes to complete, whereas when called by 2 threads it takes 4 minutes to complete.
Any ideas/suggestion/best practices to increase the performance is highly appreciated. Thanks in advance for your responses.
Some more info:
This is an application which was using threadpool earlier to perform threading in some parts. I introduced parallel tasks. Hence it's a mix up of TPL and older Thread pool. Say for example one parallel task can have multiple threads. The machine has 24 CPU cores with 64GB RAM. I ran 2 processes which would have split to 5 threads each totalling upto 10 threads. During this the process got slower. I am pasting the code here for those who would like to inspect it and provide suggestions. Sorry for pasting long code. The code may not be having all current latest features as this was coded few years ago. Thanks once again.
public static class Property11
{
/// <summary>
/// Splits agg rows to separate commands where the reference parameters are included for each command.
/// </summary>
/// <param name="worksheetID">The current worksheet ID.</param>
/// <param name="propertyID">The current property ID.</param>
/// <param name="ccrFormObj">The ccr form object.</param>
public static void SplitAggregateIncludeReferenceParameterCCRToDTH(PropertyCall propertyCallObj)
{
string worksheetID = propertyCallObj.WorksheetID;
int propertyID = propertyCallObj.PropertyID;
IDClass nextIDObj = propertyCallObj.NextIDObj;
CCRFormStructure ccrFormObj = propertyCallObj.CCRFormObj;
List<CCRFormStructure> ccrFormObjsToAdd = propertyCallObj.CCRFormObjToAddList;
DateTime dtProp = DateTime.Now;
System.Diagnostics.Debug.Print("Start time property = " + propertyCallObj.PropertyID + ", worksheet = " + propertyCallObj.WorksheetID + ": " + dtProp.ToString());
try
{
// Get all rows for worksheet
List<WorksheetRow> rowListForWorksheet =
(from wr in ccrFormObj.myWorksheetRowList
where wr.WorksheetID == worksheetID
select wr).ToList();
// Get all parameters for worksheet
List<WorksheetRowParameter> rowParameterListForWorksheet =
(from wrp in ccrFormObj.myWorksheetRowParameterList
join wr in rowListForWorksheet
on wrp.WorksheetRowID equals wr.ID
select wrp).ToList();
// Get all agg rows in worksheet
List<AggRow> aggRowsInWorksheet =
(from ar in ccrFormObj.myAggRowList
join wsrp in rowParameterListForWorksheet
on ar.WorksheetRowParameterID equals wsrp.ID
select ar).ToList();
// Get all agg row parameters in worksheet
List<AggRowParameter> aggParametersInWorksheet =
(from arp in ccrFormObj.myAggRowParameterList
join ar in aggRowsInWorksheet
on arp.AggRowID equals ar.ID
select arp).ToList();
// Get all command mappings for worksheet
List<CommandMappingObj> commandMappingListForWorksheet =
(from cm in ccrFormObj.commandMappingList
join wr in rowListForWorksheet
on cm.WorksheetRowID equals wr.ID
select cm).ToList();
// Get all parameter mappings for worksheet
List<ParameterMappingObj> parameterMappingListForWorksheet =
(from pm in ccrFormObj.parameterMappingList
join cm in commandMappingListForWorksheet
on pm.CommandMappingObjID equals cm.ID
select pm).ToList();
// Get all property objects for worksheet
List<ParameterPropertyObj> propertyList =
(from ppo in ccrFormObj.parameterPropertiesList
where ppo.ID == propertyID && ppo.WorksheetID == worksheetID
select ppo).ToList();
//List<WorksheetRow> rowsToRemove = new List<WorksheetRow>();
WorksheetRowParameter currentWorksheetRowParameter;
AggRow currentAggRow;
AggRowParameter currentAggRowParameter;
AggRow currentSteeringAggRow;
AggRowParameter currentSteeringAggRowParameter;
int newIDIndex = 0;
List<string> worksheetRowsWithoutTooLongCommandRows = new List<string>();
WorksheetRow newWSR = new WorksheetRow();
CommandMappingObj newCMO = new CommandMappingObj();
WorksheetRow newWSRForOrigRow = new WorksheetRow();
CommandMappingObj newChangeCMO = new CommandMappingObj();
List<string> steeringParameters;
IEnumerable<WorksheetRowParameter> parameterListForRow;
IEnumerable<WorksheetRowParameter> currentSteeringParameters;
string newCMOID;
ParameterMappingObj newPMO;
WorksheetRowParameter newWSRP;
string newWSRID;
string newID;
IEnumerable<string> commandsWithPropertyParameterForRow;
Hashtable htPropertyParamAndSteeringParameters = new Hashtable();
List<string> steeringParametersForProperty;
WorksheetRowParameter currentWorksheetRowPropertyParameter;
bool removeOrigRow = false;
bool firstRowForAggCreated = false;
List<WorksheetRowParameter> propParamListForFirstCreatedRow = new List<WorksheetRowParameter>();
List<string> propParamUsedAsSteeringList = new List<string>();
foreach (ParameterPropertyObj propertyParameter in propertyList)
{
if (propertyParameter.SecondaryPropertyInfo != null && propertyParameter.SecondaryPropertyInfo != "")
{
steeringParameters = propertyParameter.SecondaryPropertyInfo.Split(",".ToCharArray()).ToList();
}
else
{
steeringParameters = new List<string>();
}
htPropertyParamAndSteeringParameters.Add(propertyParameter.Parameter, steeringParameters);
}
var aggListForRow =
from ar in aggRowsInWorksheet
join arp in aggParametersInWorksheet
on ar.ID equals arp.AggRowID
select new
{
AggRow = ar,
AggRowParameter = arp
};
var worksheetRowsWithRepParam =
from wrp in rowParameterListForWorksheet
where htPropertyParamAndSteeringParameters.Contains(wrp.Parameter)
join al in aggListForRow
on wrp.ID equals al.AggRow.WorksheetRowParameterID
into aggList
where aggList.Count() > 0
select new
{
WorksheetRowParameter = wrp,
AggList = aggList
};
foreach (WorksheetRow worksheetRow in rowListForWorksheet.ToList())
{
var worksheetRowWithRepParam =
worksheetRowsWithRepParam.Where(wrp => wrp.WorksheetRowParameter.WorksheetRowID == worksheetRow.ID);
if (worksheetRowWithRepParam.Count() > 0)
{
firstRowForAggCreated = false;
var currentMappingList =
from cmo in commandMappingListForWorksheet
where cmo.WorksheetRowID == worksheetRow.ID
join pmo in parameterMappingListForWorksheet
on cmo.ID equals pmo.CommandMappingObjID
into parameterMappingList
select new
{
CommandMapping = cmo,
ParameterMappingList = parameterMappingList
};
IEnumerable<ParameterPropertyObj> sortedPropertyList =
from wrwrp in worksheetRowWithRepParam
join ppo in propertyList
on wrwrp.WorksheetRowParameter.Parameter equals ppo.Parameter
orderby wrwrp.AggList.Count() descending
select ppo;
propParamUsedAsSteeringList.Clear();
foreach (ParameterPropertyObj ppo in sortedPropertyList)
{
if (!propParamUsedAsSteeringList.Contains(ppo.Parameter))
{
var currentWorksheetRowsWithRepParam =
worksheetRowWithRepParam.Where(p => p.WorksheetRowParameter.Parameter == ppo.Parameter);
if (currentWorksheetRowsWithRepParam.Count() == 0)
{
continue;
}
var currentWorksheetRowWithRepParam = currentWorksheetRowsWithRepParam.ElementAt(0);
var currentAggList = currentWorksheetRowWithRepParam.AggList;
if (!firstRowForAggCreated)
{
currentWorksheetRowPropertyParameter = currentWorksheetRowWithRepParam.WorksheetRowParameter;
}
else
{
currentWorksheetRowPropertyParameter = propParamListForFirstCreatedRow.Where(p => p.Parameter == ppo.Parameter).ElementAt(0);
}
if (currentAggList.Count() > 1)
{
removeOrigRow = true;
steeringParametersForProperty = (List<string>)htPropertyParamAndSteeringParameters[ppo.Parameter];
currentSteeringParameters =
from wrp in rowParameterListForWorksheet
where wrp.WorksheetRowID == worksheetRow.ID
&& steeringParametersForProperty.Contains(wrp.Parameter)
select wrp;
commandsWithPropertyParameterForRow =
from cml in currentMappingList
where cml.ParameterMappingList.Count(pmo => pmo.Name == ppo.Parameter) > 0
select cml.CommandMapping.Name;
propParamUsedAsSteeringList.AddRange(
from sp in sortedPropertyList
where sp.Parameter != ppo.Parameter
join csp in currentSteeringParameters
on sp.Parameter equals csp.Parameter
select csp.Parameter);
// CREATE NEW WORKSHEET ROWS FOR EACH BUT THE FIRST AGG ROW PARAMETER
for (int i = 0; i < currentAggList.Count(); i++)
{
currentAggRow = currentAggList.ElementAt(i).AggRow;
currentAggRowParameter = currentAggList.ElementAt(i).AggRowParameter;
if (i == 0)
{
currentWorksheetRowPropertyParameter.Value = currentAggRowParameter.Value;
if (!firstRowForAggCreated)
{
propParamListForFirstCreatedRow.Clear();
newWSRID = newIDIndex.ToString().PadLeft(3, '0');
newID = newWSRID;
if (!worksheetRow.ID.Contains(','))
{
newID = "," + newWSRID;
}
newWSRForOrigRow = new WorksheetRow
{
ID = worksheetRow.ID + newID,
OriginalWorksheetRowID = worksheetRow.OriginalWorksheetRowID,
WorksheetID = worksheetRow.WorksheetID
};
ccrFormObj.myWorksheetRowList.Add(newWSRForOrigRow);
parameterListForRow =
from wrp in rowParameterListForWorksheet
where wrp.WorksheetRowID == worksheetRow.ID
select wrp;
foreach (WorksheetRowParameter currentParameter in parameterListForRow)
{
newID = "";
if ((currentParameter.ID != null) && (!currentParameter.ID.Contains(',')))
{
newID = ",";
}
newID += newIDIndex.ToString().PadLeft(3, '0');
newWSRP = new WorksheetRowParameter
{
ID = currentParameter.ID + newID,
OriginalParameterID = currentParameter.OriginalParameterID,
WorksheetRowID = newWSRForOrigRow.ID,
Parameter = currentParameter.Parameter,
Value = currentParameter.Value,
Disabled = currentParameter.Disabled
};
if (htPropertyParamAndSteeringParameters.Contains(newWSRP.Parameter)
&& newWSRP.Parameter != ppo.Parameter)
{
// TODO: IF AGG, TAKE AGG POS VALUE
var steeringParamAggList =
from wrwrp in worksheetRowWithRepParam
where wrwrp.WorksheetRowParameter.Parameter == newWSRP.Parameter
select wrwrp.AggList;
if (steeringParamAggList.Count() > 0)
{
if (steeringParamAggList.ElementAt(0).Count() > i)
{
currentSteeringAggRow = steeringParamAggList.ElementAt(0).ElementAt(i).AggRow;
currentSteeringAggRowParameter = steeringParamAggList.ElementAt(0).ElementAt(i).AggRowParameter;
newWSRP.Value = currentSteeringAggRowParameter.Value;
ccrFormObj.myAggRowParameterList.Remove(currentSteeringAggRowParameter);
ccrFormObj.myAggRowList.Remove(currentSteeringAggRow);
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
}
}
else
{
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
}
propParamListForFirstCreatedRow.Add(newWSRP);
}
else
{
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
}
}
foreach (var currentMapping in currentMappingList)
{
// Re-point original command mapping to new row
newCMOID = newIDIndex.ToString().PadLeft(3, '0');
if (!currentMapping.CommandMapping.ID.Contains(','))
{
newID = "," + newCMOID;
}
// Create new command mapping object
newCMO = new CommandMappingObj
{
ID = currentMapping.CommandMapping.ID + newID,
Name = currentMapping.CommandMapping.Name,
WorksheetRowID = newWSRForOrigRow.ID
};
ccrFormObj.commandMappingList.Add(newCMO);
foreach (ParameterMappingObj pmo in currentMapping.ParameterMappingList)
{
newPMO = new ParameterMappingObj
{
Name = pmo.Name,
CommandMappingObjID = newCMO.ID
};
ccrFormObj.parameterMappingList.Add(newPMO);
}
}
firstRowForAggCreated = true;
}
}
else
{
newWSRID = newIDIndex.ToString().PadLeft(3, '0');
newID = newWSRID;
if (!worksheetRow.ID.Contains(','))
{
newID = "," + newWSRID;
}
newWSR = new WorksheetRow
{
ID = worksheetRow.ID + newID,
OriginalWorksheetRowID = worksheetRow.OriginalWorksheetRowID,
WorksheetID = worksheetRow.WorksheetID
};
ccrFormObj.myWorksheetRowList.Add(newWSR);
foreach (WorksheetRowParameter currentSteeringParameter in currentSteeringParameters)
{
newID = "";
if ((currentSteeringParameter.ID != null) && (!currentSteeringParameter.ID.Contains(',')))
{
newID = ",";
}
newID += newIDIndex.ToString().PadLeft(3, '0');
newWSRP = new WorksheetRowParameter
{
ID = currentSteeringParameter.ID + newID,
OriginalParameterID = currentSteeringParameter.OriginalParameterID,
WorksheetRowID = newWSR.ID,
Parameter = currentSteeringParameter.Parameter,
Value = currentSteeringParameter.Value,
Disabled = currentSteeringParameter.Disabled
};
var steeringParamAggList =
from wrwrp in worksheetRowWithRepParam
where wrwrp.WorksheetRowParameter.Parameter == newWSRP.Parameter
select wrwrp.AggList;
if (steeringParamAggList.Count() > 0)
{
if (steeringParamAggList.ElementAt(0).Count() > i)
{
currentSteeringAggRow = steeringParamAggList.ElementAt(0).ElementAt(i).AggRow;
currentSteeringAggRowParameter = steeringParamAggList.ElementAt(0).ElementAt(i).AggRowParameter;
newWSRP.Value = currentSteeringAggRowParameter.Value;
ccrFormObj.myAggRowParameterList.Remove(currentSteeringAggRowParameter);
ccrFormObj.myAggRowList.Remove(currentSteeringAggRow);
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
}
}
else
{
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
}
}
// Add rep param
newID = "";
if ((currentWorksheetRowPropertyParameter.ID != null) && (!currentWorksheetRowPropertyParameter.ID.Contains(',')))
{
newID = ",";
}
newID += newIDIndex.ToString().PadLeft(3, '0');
newWSRP = new WorksheetRowParameter
{
ID = currentWorksheetRowPropertyParameter.ID + newID,
OriginalParameterID = currentWorksheetRowPropertyParameter.OriginalParameterID,
WorksheetRowID = newWSR.ID,
Parameter = currentWorksheetRowPropertyParameter.Parameter,
Value = currentAggRowParameter.Value,
Disabled = currentWorksheetRowPropertyParameter.Disabled
};
ccrFormObj.myWorksheetRowParameterList.Add(newWSRP);
foreach (var currentMapping in currentMappingList)
{
if (commandsWithPropertyParameterForRow.Contains(currentMapping.CommandMapping.Name))
{
newCMOID = newIDIndex.ToString().PadLeft(3, '0');
if (!currentMapping.CommandMapping.ID.Contains(','))
{
newID = "," + newCMOID;
}
// Create new command mapping object
newCMO = new CommandMappingObj
{
ID = currentMapping.CommandMapping.ID + newID,
Name = currentMapping.CommandMapping.Name,
WorksheetRowID = newWSR.ID
};
ccrFormObj.commandMappingList.Add(newCMO);
foreach (ParameterMappingObj pmo in currentMapping.ParameterMappingList)
{
if ((pmo.Name == ppo.Parameter) || (currentSteeringParameters.Count(p => p.Parameter == pmo.Name) > 0))
{
newPMO = new ParameterMappingObj
{
Name = pmo.Name,
CommandMappingObjID = newCMO.ID
};
ccrFormObj.parameterMappingList.Add(newPMO);
}
}
}
}
}
newIDIndex++;
ccrFormObj.myAggRowParameterList.Remove(currentAggRowParameter);
ccrFormObj.myAggRowList.Remove(currentAggRow);
}
}
else
{
currentAggRow = currentAggList.ElementAt(0).AggRow;
currentAggRowParameter = currentAggList.ElementAt(0).AggRowParameter;
currentWorks
There is nothing intrinsic about a static method that would make it slower when accessed by multiple threads, unless for example:
you have some shared resource that is being a throttle, such as a shared lock, or going to a single UI thread
you have more threads than available CPU cores, and it is simply having to do lots of switching
you are maxing out some other resource, such as disk IO, network IO, etc
Since you haven't posted any code, we can only guess.