Pass a property of a class as a parameter?
To pass in a string property name you're going to have to use reflection, which is not compile-time safe and is relatively slow. A more type-safe way would be to pass in a delegate instead of a property name:
public static bool IdenticalValues<T>(this List<Product> matchedProducts, Func<Product, T> matchExpression)
{
var itemToMatch = matchedProducts.First();
if (matchedProducts.All(p => EqualityComparer<T>.Default.Equals(matchExpression(p), matchExpression(itemToMatch))))
return true;
else
return false;
}
And call it like this:
bool allMatch = collection.IdenticalValues(i => i.PROPERTYTHATCANCHANGE );
You could even make it completely generic:
public static bool IdenticalValues<TEntity, TProperty>(this IEnumerable<TEntity> matchedEntities, Func<TEntity, TProperty> matchExpression)
{
var itemToMatch = matchedEntities.First();
if (matchedEntities.All(p => EqualityComparer<TProperty>.Default.Equals(matchExpression(p), matchExpression(itemToMatch))))
return true;
else
return false;
}
How to expect property type of a class as parameter in a generic function?
Try using expression trees:
static void GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> property) {
var propertyInfo = (property.Body as MemberExpression).Member;
Console.WriteLine(propertyInfo.Name);
}
You can call it like this:
GetPropertyName((Car c) => c.DoorsCount);
Obviously, this will throw an exception if you try to pass in stuff that isn't a member expression.
You can't use nameof
here because nameof
is a constant expression. The expression in the brackets is not evaluated. The whole nameof
expression is evaluated at compile time. This means that trying to get the name of the property passed as a parameter will just get you the name of the parameter. You need something that works at runtime, like expression trees.
Also note that you don't need an instance of Car
here. What the method needs is just an expression. No property's value is actually evaluated.
Passing property as parameter
You almost have the right solution already - the only missing piece is how you use the MeritFunctionLine.property
property to get the desired value from the CalculationOutput
.
In your foreach
loop, simply replace the calculation line with
m += Math.Abs(item.property(values) - item.value);
Edit:
Adding Genericity
To address Obsidian Phoenix's comment, you can use this with different classes by making both MeritFunction
and MeritFunctionLine
generic, so:
public class MeritFunctionLine<TCalcOutput>
{
public Func<TCalcOutput, double> property { get; set; }
public double value { get; set; }
public ComparisonTypes ComparisonType { get; set; }
}
public class MeritFunction<TCalcOutput>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
}
The rewritten usage example would be
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>();
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property1, value = 90, comparisonType = ComparisonTypes.GreaterThan });
mf.Lines.Add(new MeritFunctionLine<CalculationOutput>() { property = x => x.Property3, value = 50, comparisonType = ComparisonTypes.Equals });
CalculationOutput c1 = new CalculationOutput() { property1 = 1, property2 = 20, property3 = 150, property4 = 500 };
CalculationOutput c2 = new CalculationOutput() { property1 = 15, property2 = 32, property3 = 15, property4 = 45 };
double value1 = mf.Calculate(c1);
double value2 = mf.Calculate(c2);
Some extra convenience
If you have many MeritFunctionLine
s to add, the syntax above can be a bit tedious. So as a bonus, let's change MeritFunction
so that it can be initialized with the list initialization syntax. To do that, we need to make it IEnumerable
and give it an Add
function:
public class MeritFunction<TCalcOutput> : IEnumerable<MeritFunctionLine<TCalcOutput>>
{
public List<MeritFunctionLine<TCalcOutput>> Lines { get; set; }
public MeritFunction()
{
Lines = new List<MeritFunctionLine<TCalcOutput>>();
}
public void Add(Func<TCalcOutput, double> property, ComparisonTypes ComparisonType, double value)
{
Lines.Add(new MeritFunctionLine<CalculationOutput>
{
property = property,
value = value,
comparisonType = ComparisonType
});
}
public double Calculate(TCalcOutput values)
{
double m = 0;
foreach (var item in Lines)
{
m += Math.Abs(item.property(values) - item.value);
}
return m;
}
public IEnumerator<MeritFunctionLine<TCalcOutput>> GetEnumerator()
{
return List.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Note that the Add
method receives the parameters in a different order - you'll understand why when you look at the usage. Quite a bit of extra code, but now creating our MeritFunction
is a bit nicer:
MeritFunction<CalculationOutput> mf = new MeritFunction<CalculationOutput>
{
{ x => x.Property1, ComparisonTypes.GreaterThan, 90 },
{ x => x.Property3, ComparisonTypes.Equals, 50 }
};
Note, all code untested. Use at own risk :)
Passing property type as parameter
I'm not sure this is what you want, but using closure:
func f1<T>(car: Car, getter: Car -> T) {
println(getter(car))
}
let c1 = Car()
f1(c1, {$0.doors})
f1(c1, {$0.price})
Is there a way to pass a class property as a parameter to a method?
Full solution after edits:
public static class Extensions
{
//create other overloads
public static void MyCheckMethodDate<TObj>(this TObj obj,Expression<Func<TObj,DateTime>> property, string value)
{
obj.MyCheckMethod(property, value, DateTime.Parse);
}
public static void MyCheckMethod<TObj,TProp>(this TObj obj,Expression<Func<TObj,TProp>> property, string value,Func<string, TProp> converter)
{
if(string.IsNullOrEmpty(value))
return;
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if(null != propertyInfo && propertyInfo.CanWrite)
{
propertyInfo.SetValue(obj, converter(value));
}
}
}
public class Obj
{
public object Prop1{get;set;}
public string Prop2{get;set;}
public DateTime Prop3{get;set;}
}
public class Program
{
public static void Main()
{
var obj = new Obj();
obj.MyCheckMethodDate(x=>x.Prop3, "2018-1-1");
Console.WriteLine(obj.Prop3);
}
}
How could I pass a propertyInfo.PropertyType as an Datatype to instanciate a variable and pass it to a generic function?
Using the answer here as reference, you could do this:
foreach (var propInfo in propInfos)
{
var method = result.GetType().GetMethod(nameof(result.Read));
var generic = method.MakeGenericMethod(propInfo.PropertyType.GetEnumUnderlyingType());
var propValue = generic.Invoke(result, null);
propInfo.SetValue(res, propValue);
}
Get property type to pass to generic method
You can use MakeGenericMethod, performance is actually quite reasonable and allows you to explicitly define what you are calling with what, so reduces the overhead.
So something like the following, the Invoker would call the explicit method / class you need, and the helper actually invokes the generic call.
public class GenericHelper
{
public static void DoSomethingGeneric(GenericInvokerParameters parameters)
{
var targetMethodInfo = typeof(GenericInvoker).GetMethod("DoSomethingGeneric");
var genericTargetCall = targetMethodInfo.MakeGenericMethod(parameters.InvokeType);
genericTargetCall.Invoke(new GenericInvoker(), new[] { parameters });
}
}
public class GenericInvoker
{
public void DoSomethingGeneric<T>(GenericInvokerParameters parameters)
{
//Call your generic class / method e.g.
SomeClass.SomeGenericMethod<T>(parameters.SomeValue);
}
}
public class GenericInvokerParameters
{
public GenericInvokerParameters(Type typeToInvoke, string someValue)
{
SomeValue = someValue;
InvokeType = typeToInvoke;
}
public string SomeValue { get; private set; }
public Type InvokeType { get; private set; }
}
Passing property list as strongly typed parameters
I am completely agree with @Patrick and its preferred way over mine.
but if you say you are not using the C#6.0 and then you can use the code you have written. I just use the param
, yield return
and one foreach
loop
private static IEnumerable<string> GetPropertyName<TObj, TProp>(params Expression<Func<TObj, TProp>>[] propCollection)
{
foreach (var prop in propCollection)
{
var expression = prop.Body as MemberExpression;
if (expression != null)
{
var property = expression.Member as PropertyInfo;
if (property != null)
{
yield return property.Name;
}
}
yield return string.Empty;
}
}
UPDATE
First One ask you to specific the type of the object again and again mean you have to provide the full length expression again.
Try the below it will ask you to specify the property as much as you want in one Expression only.
public static IEnumerable<string> GetPropertiesName<TObj, TProp>(Expression<Func<TObj, TProp[]>> prop)
{
var array = (prop.Body as NewArrayExpression);
var exp = array == null ? null : array.Expressions;
if (exp != null)
{
//var expArr = (prop.Body as NewArrayExpression).Expressions;
foreach (var oneProp in exp)
{
Expression onePropExp;
if (oneProp.GetType() == typeof (UnaryExpression))
{
onePropExp = (oneProp as UnaryExpression).Operand;
}
else
{
onePropExp = oneProp;
}
var property = (onePropExp as MemberExpression).Member as PropertyInfo;
if (property != null)
{
yield return property.Name;
}
yield return string.Empty;
}
}
yield return string.Empty;
}
You can call it like -
var propNames = GetPropertiesName((AllSubsTransAndDevices d) => new[]
{
d.CurrentDriverId,
d.GPSDevicesId,
d.TransporterId
});
Passing a class property as a parameter
You could use a Func<Flags, bool>
as parameter:
public Flags[] getAllWhereAisTrue(Flags[] array, Func<Flags, bool> propertySelector)
{
List<Flags> resultList = new List<Flags>();
for (int i = 0; i < array.Length; i++)
{
if (propertySelector(array[i])) // for each Flags for which a is true
{ // add it to the results list
resultList.Add(array[i]);
}
}
return resultList.ToArray(); //return the results list as an array
}
Then you could use it like this:
var allAFlagsSet = getAllWhereAisTrue(flagsArray, x=> x.a);
But really you should not reinvent this - Linq does this out of the box (notice the similarity):
var allAFlagsSet = flagsArray.Where(x=> x.a).ToArray();
Both solutions would require the a,b,c to be public (should really be a public property in this case)
Related Topics
Xcodebuild -Exportarchive Wont Allow Me to Specify Filename
Swift Array to Array of Tuples
Swift: Reflecting Properties of Subclass of Nsmanagedobject
Swift: Print Name of a Function Stored in a Variable
Swift: Nstextfield Allow Only Specified Characters
How to Increase the Scope of Variables in Switch-Case/Loops in Swift
Xcode Build Perfect Failure -- Copenssl Not Found
Xcframework Issue, a Library with the Identifier "Ios-Armv7_Arm64" Already Exists
How Would I Create a Constant That Could Be One of Several Strings Depending on Conditions
Generic Within a Generic in Swift
How to Add Document with Custom Id to Firebase (Firestore) on Swift
Ios: Ambiguous Use of Init(Cgimage)
Implemented Helper App But Does Not Launch on Login
Swift: How to Flush Stdout After Println
How Convert a *Positive* Number into an Array of Digits in Swift