Passing Property Type as Parameter

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 MeritFunctionLines 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



Leave a reply



Submit