I want to pass an instance of a class to a method using an Expression<T> and retrieve the instance from within the method:
public class MyOb
{
public void Receive(Expression<Func<Item>> item)
{
// Here I would like to get item as Item
// Have tried various things such as
var x = ((MemberExpression)(item.Body)).Member;
int y = x.IntProp // should be 123.
}
}
public class Item
{
public int IntProp { get; set; } = 123;
}
MyOb mo = new();
Item myItem = new();
mo.Receive(() => myItem);
That would not be that easy cause compiler will generate a special class to handle closure which will store the value of the local variable (myItem). Something like this should do the trick:
public static void Receive(Expression<Func<Item>> item)
{
if (item.Body is MemberExpression { Expression: ConstantExpression constE })
{
var itemValue = constE.Type.GetFields()
.Where(fi => fi.FieldType.IsAssignableTo(typeof(Item)))
.Single() // possibly proper validation message if multiple found
.GetValue(constE.Value);
var intProp = ((Item)itemValue).IntProp; // your value
Console.WriteLine(intProp); // will print 123 for your code
}
}
Related
I have a collection containing variables I want to print the names of the variable in this collection
screenshot
The method that returns the type is working, I couldn't find the name
How can I create a function to do this where it says GetName() below
var collection = new List<object> { b,sb,shrt,ushrt,i,ui,lng,ulng,f,d,dec,c,str,b1,b2,b3,o1,o2,o3,o4,dt };
foreach (object item in collection)
{
Console.WriteLine("variable name : "+item.GetName() );
Console.WriteLine("variable value: "+item);
Console.WriteLine("variable type : "+ item.GetType().Name);
Console.WriteLine("***************************** ");
}
The list does not contain the variables.
It contains either:
the values variable or
the references that the variable points to
Please see Value types (C# reference).
What you are trying to achieve is not possible without significant changes to the code.
The best I could do is the following.
Result
b: True, Boolean
sb: , StringBuilder
shrt: 7, Int16
Code
using System.Linq.Expressions;
using System.Text;
bool b = true;
StringBuilder sb = new ();
short shrt = 7;
var l = new List<(string Name,object V)> { Get(() => b), Get(() => sb), Get(() => shrt) };
foreach(var x in l)
Console.WriteLine($"{x.Name}: {x.V}, {x.V.GetType().Name}" );
static (string Name ,T V) Get<T>(Expression<Func<T>> memberExpression)
{
// https://stackoverflow.com/questions/2616638/access-the-value-of-a-member-expression
// with modifications
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
var lambda = Expression.Lambda<Func<T>>(expressionBody);
var getter = lambda.Compile();
var v = getter();
return (expressionBody.Member.Name, v);
}
Your code shows iterating a collection of type Object and we know for sure that class Object does have a GetType() method and _doesn't_ have a GetName() method or Name property. But the item in your collection most likely isn't "really" an object, it's some class that (like every class) inherits Object. You seem confident that this unknown class has a Name property but even if the object type of item is completely unknown, you can use System.Reflection to print out the Name if it exists and an error readout if it doesn't.
Wrapping this reflection in an Extension Method is of the easier ways to answer your question:
[...]How can I create a function to do this where it says GetName()
static partial class Extensions
{
public static string GetName(this object unk)
{
Type type = unk.GetType();
PropertyInfo propertyInfo = type.GetProperty("Name");
if (propertyInfo == null)
{
return $"Error: Type '{type.Name}' does not have a Name property.";
}
else
{
return $"{propertyInfo.GetValue(unk)}";
}
}
}
MOCK
With the extension in place, you can now use the syntax shown in your post.
var collection = new object []
{
new HasNameA(),
new HasNameB(),
new NoNameC(),
};
foreach (object item in collection)
{
Console.WriteLine("variable name : " + item.GetName());
Console.WriteLine("variable value: " + item);
Console.WriteLine("variable type : " + item.GetType().Name);
Console.WriteLine("***************************** ");
}
Where:
class HasNameA
{
public string Name { get; set; } = "A";
public object Value { get; set; } = 1;
public override string ToString() => $"{Value}";
}
class HasNameB
{
public string Name { get; set; } = "B";
public object Value { get; set; } = "Hello";
public override string ToString() => $"{Value}";
}
class NoNameC
{
public object Value { get; set; } = Math.PI;
public override string ToString() => $"{Value}";
}
I have a following problem. I have to iterate over all the properties of the class to configure some builder. A class has a lot of properties, so the code is cumbersome. It looks like this:
var b = builder<MyTypeWith1000Properties>
.WithProperty(x=>x.Property1)
.WithProperty(x=>x.Property2)
...
.WithProperty(x=>x.Property1000);
The code is repeated in many places for many differend types, not only MyTypeWith1000Properties. I was thinking about creating some extension, like this:
var b = builder<MyTypeWith1000Properties>
.WithAllProperties();
and then in the WithAllProperties I could iterate over type properties using Reflection, like this:
public static IDataExtractor<T> WithAllProperties(this IDataExtractor<T> extractor)
{
var properties = typeof(T).GetProperties();
foreach (var property in properties)
{
extractor = extractor.WithProperty(/*the problem is here/*);
}
return extractor;
}
How to convert the property variable in the loop to a corresponding expression
Expression<Func<TRow, TValue>> propertyExpression
as this is what WithProperty expects
Update - Setting the correct parameter value in Expression.Lambda>
You could try something like this
public static class BuilderExtension
{
public static IDataExtractor<T> WithAllProperties<T>(this IDataExtractor<T> extractor)
{
var properties = typeof(T).GetProperties();
foreach (var propertyInfo in properties)
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = Expression.Property(parameter, propertyInfo);
var lambda = Expression.Lambda<Func<T, object>>(property,parameter);
extractor = extractor.WithProperty(lambda);
}
return extractor;
}
}
Suppose you have the following class structures
public class MyTypeWith100Properties
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
public string Property4 { get; set; }
}
public interface IDataExtractor<T>
{
IDataExtractor<T> WithProperty(Expression<Func<T, object>> expr);
}
public class DataExtractor<T> : IDataExtractor<T>
{
public List<Expression<Func<T, object>>> Expressions { get; private set; }
public DataExtractor() {
Expressions = new List<Expression<Func<T, object>>>();
}
public IDataExtractor<T> WithProperty(Expression<Func<T, object>> expr)
{
Expressions.Add(expr);
return this;
}
}
Then if you run this
var builder = new DataExtractor<MyTypeWith100Properties>();
var b = builder.WithAllProperties<MyTypeWith100Properties>()
as DataExtractor<MyTypeWith100Properties>;
var instance = new MyTypeWith100Properties() {
Property1 = "This is property 1",
Property2 = "This is property 2",
Property3 = "This is property 3",
Property4 = "This is property 4"
};
foreach (var current in b.Expressions)
{
var compiled = current.Compile();
var result = compiled.Invoke(instance);
Console.WriteLine(result);
}
Your output will be
This is property 1
This is property 2
This is property 3
This is property 4
WithProperty is a generic method that takes a type parameter implied by the result type of the property member access expression, which is what TValue represents in its declaration. Since you want to use reflection to generate the lambdas, you have to do the WithProperty call dynamically as well so you can call the one with the proper type (e.g. WithProperty<String> for a String property).
Here is an extension method that generates a lambda that consists of all the chained WithProperty calls for all the properties in a class, and then compiles and calls the lambda on the IDataExtractor. I chained all the calls together and then compiled because there might be some overhead for compiling, so I didn't want to compile and call code for each property individually.
public static class IDataExtractorExt {
public static IDataExtractor<TRow> WithAllProperties<TRow>(this IDataExtractor<TRow> extractor) {
var p = Expression.Parameter(typeof(IDataExtractor<TRow>), "p"); // final lambda parameter
Expression ansBody = p; // start with p => p
var withPropGenericMI = typeof(IDataExtractor<TRow>).GetMethod("WithProperty"); // lookup WithProperty<> generic method
var properties = typeof(TRow).GetProperties();
foreach (var property in properties) {
var withPropMI = withPropGenericMI.MakeGenericMethod(property.PropertyType); // instantiate generic WithProperty<> to property type
var pxp = Expression.Parameter(typeof(TRow), "x"); // property accessor lambda parameter
var pxb = Expression.PropertyOrField(pxp, property.Name); // property accessor expression x.property
Expression propExpr = Expression.Lambda(pxb, pxp); // x => x.property
ansBody = Expression.Call(ansBody, withPropMI, propExpr); // build up p => p.WithProperty(x => x.property)...
}
return ((IDataExtractor<TRow>)(Expression.Lambda(ansBody, p).Compile().DynamicInvoke(extractor)));
}
}
I am at a complete loss here, despite looking at multiple SO posts and anything else I can think of.
My goal here is to make a really, really simple mapper. Something I can basically use as a tool in some unit tests. It doesn't need to be sophisticated or anything -- just map high-level primitive and string values of one object to another. So the basic algorithm is:
Get all properties from TFrom
Get all properties from TTo
Get all properties that are in both, matched by name.
I know this could be a bug in that they could have the same name but a different type, but let's set that aside. It's not what I'm running into here -- the properties and types match between classes.
Create an instance of TTo that we can copy to.
For each property that was mapped between the objects:
Get the value off of the from object
Convert the value to the type of the property
Set the value on the to object
The problem is that no matter what I do, and no matter what the type of the property is (int or string, for example) I get the following:
Object does not match the target type.
Here is the code I'm using:
public TTo Map<TFrom, TTo>(TFrom from)
{
if (from == null) return default;
var fromProps = GetProperties(typeof(TFrom));
var toProps = GetProperties(typeof(TTo));
// Props that can be mapped from one to the other
var propsToCopy = fromProps.Intersect(toProps, new PropertyComparer()).ToList();
var returnObject = (TTo)Activator.CreateInstance(typeof(TTo));
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(returnObject, convertedValue, null);
}
return returnObject;
}
public PropertyInfo[] GetProperties(Type objectType)
{
var allProps = objectType.GetProperties(
BindingFlags.Public | BindingFlags.Instance);
return allProps.Where(p => p.PropertyType.IsPrimitive ||
p.PropertyType == typeof(string)).ToArray();
}
private class PropertyComparer : IEqualityComparer<PropertyInfo>
{
public bool Equals(PropertyInfo x, PropertyInfo y)
{
return x.Name.Equals(y.Name);
}
public int GetHashCode(PropertyInfo obj)
{
return obj.Name.GetHashCode();
}
}
And here's an example of a way I would call it, with sample classes:
public class Foo
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
public class FooOther
{
public string StringProp { get; set; }
public int IntProp { get; set; }
}
var foo = new Foo { IntProp = 1, StringProp = "foo" };
var mappedFoo = Map<Foo, FooOther>(foo);
About the only hint I've gotten out of Visual Studio is from the watch window: if the property type is a string, the watch window reports the type of convertedValue as object. If the property type is an int, the watch window reports object {int}.
The PropertyInfo you are using is still coupled to the type the property it is representing is a member of, so you aren't able to use it to set the value of an object of another type without the error you are getting.
Here's a shortened example of the behavior:
public class A {
public string Id {get;set;}
}
public class B {
public string Id {get;set;}
}
void Main()
{
var test = new A() { Id = "Test"};
var prop = test.GetType().GetProperty("Id");
var b = (B)Activator.CreateInstance(typeof(B));
var fromValue = prop.GetValue(test);
var converted = Convert.ChangeType(fromValue, prop.PropertyType);
prop.SetValue(b, converted, null); // Exception
}
This makes sense if you think of the PropertyInfo as a member of A. To fix this, you'll want to get a property info that's specific to your type. I can fix up my example with the following:
var propTo = typeof(B).GetProperty(prop.Name);
propTo.SetValue(b, converted, null);
Console.WriteLine(b.Id); // Output: Test
Bringing that together, if you change the contents of your foreach to the following you should be in the clear:
foreach (var prop in propsToCopy)
{
// Copy the values
var fromValue = prop.GetValue(from, null);
var convertedValue = Convert.ChangeType(fromValue, prop.PropertyType);
var propTo = typeof(TTO).GetProperty(prop.Name);
propTo.SetValue(returnObject, convertedValue, null);
}
I have 2 objects (a DTO and a Entry Framework Object) and I want to update the EF Entity if the corresponding value in the DTO is different. I envisage it would be done like this:
UpdateIfDifferent(dtoObject, x => x.MailStatus, efObject, r => r.MailStatus);
UpdateIfDifferent(dtoObject, x => x.WebStatus, efObject, r => r.WebStatus);
private void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
(TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty,
TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
{
if (!originalProperty.Compile()(original).Equals(updatedProperty.Compile()(updated))) {
// how do I assign back to original from updated?
}
}
}
I believe the following should work but I do not know how I can assign the value back to the efObject. Can anybody clue me in please?
You can do like this:
public static void Main()
{
var original = new TestEntity();
original.Name = "test";
var dto = new TestDTO();
dto.FirstName = "New Value";
UpdateIfDifferent(original, o => o.Name, dto, d => d.FirstName);
Console.WriteLine(original.Name);
}
private static void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
(TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty,
TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
{
if (!originalProperty.Compile()(original).Equals(updatedProperty.Compile()(updated)))
{
var updatedMember = (updatedProperty.Body as MemberExpression).Member as PropertyInfo;
var updatedValue = updatedMember.GetValue(updated);
var member = (originalProperty.Body as MemberExpression).Member as PropertyInfo;
member.SetValue(original, updatedValue);
}
}
public class TestEntity
{
public string Name {get;set;}
}
public class TestDTO
{
public string FirstName {get;set;}
}
Here is a working sample on DotNetFiddle - https://dotnetfiddle.net/k1qLZH
This sample is just for property and member access. So if your expression will have another signature like method,sub property, field access then this code will fail.
Another approach without lambda compilation that takes some time might be:
private static void UpdateIfDifferent<TOriginal, TOriginalProperty, TUpdated, TUpdatedProperty>
(TOriginal original, Expression<Func<TOriginal, TOriginalProperty>> originalProperty,
TUpdated updated, Expression<Func<TUpdated, TUpdatedProperty>> updatedProperty)
{
var updatedMember = (updatedProperty.Body as MemberExpression).Member as PropertyInfo;
var updatedValue = updatedMember.GetValue(updated);
var originalMember = (originalProperty.Body as MemberExpression).Member as PropertyInfo;
var originalValue = originalMember.GetValue(original);
if (!object.Equals(updatedValue, originalValue))
originalMember.SetValue(original, updatedValue);
}
F# has a convenient feature "with", example:
type Product = { Name:string; Price:int };;
let p = { Name="Test"; Price=42; };;
let p2 = { p with Name="Test2" };;
F# created keyword "with" as the record types are by default immutable.
Now, is it possible to define a similar extension in C#?
seems it's a bit tricky, as in C# i'm not sure how to convert a string
Name="Test2"
to a delegate or expression?
public static T With<T, U>(this T obj, Expression<Func<T, U>> property, U value)
where T : ICloneable {
if (obj == null)
throw new ArgumentNullException("obj");
if (property == null)
throw new ArgumentNullException("property");
var memExpr = property.Body as MemberExpression;
if (memExpr == null || !(memExpr.Member is PropertyInfo))
throw new ArgumentException("Must refer to a property", "property");
var copy = (T)obj.Clone();
var propInfo = (PropertyInfo)memExpr.Member;
propInfo.SetValue(copy, value, null);
return copy;
}
public class Foo : ICloneable {
public int Id { get; set; }
public string Bar { get; set; }
object ICloneable.Clone() {
return new Foo { Id = this.Id, Bar = this.Bar };
}
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = foo.With(x => x.Bar, "boo-ya");
Console.WriteLine(newFoo.Bar); //boo-ya
}
Or, using a copy constructor:
public class Foo {
public Foo(Foo other) {
this.Id = other.Id;
this.Bar = other.Bar;
}
public Foo() { }
public int Id { get; set; }
public string Bar { get; set; }
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = new Foo(foo) { Bar = "boo-ya" };
Console.WriteLine(newFoo.Bar);
}
And a slight variation on George's excellent suggestion, that allows for multiple assignments:
public static T With<T>(this T obj, params Action<T>[] assignments)
where T : ICloneable {
if (obj == null)
throw new ArgumentNullException("obj");
if (assignments == null)
throw new ArgumentNullException("assignments");
var copy = (T)obj.Clone();
foreach (var a in assignments) {
a(copy);
}
return copy;
}
public static void Test() {
var foo = new Foo { Id = 1, Bar = "blah" };
var newFoo = foo.With(x => x.Id = 2, x => x.Bar = "boo-ya");
Console.WriteLine(newFoo.Bar);
}
I would probably use the second one since (1) any general purpose solution is going to be unnecessarily slow and convoluted; (2) it has the closest syntax to what you want (and the syntax does what you expect); (3) F# copy-and-update expressions are implemented similarly.
Maybe something like this:
void Main()
{
var NewProduct = ExistingProduct.With(P => P.Name = "Test2");
}
// Define other methods and classes here
public static class Extensions
{
public T With<T>(this T Instance, Action<T> Act) where T : ICloneable
{
var Result = Instance.Clone();
Act(Result);
return Result;
}
}
As an alternative to lambda function, you can use parameters with default values. The only minor issue is that you have to pick some default value that means do not change this parameter (for reference types), but null should be a safe choice:
class Product {
public string Name { get; private set; }
public int Price { get; private set; }
public Product(string name, int price) {
Name = name; Price = price;
}
// Creates a new product using the current values and changing
// the values of the specified arguments to a new value
public Product With(string name = null, int? price = null) {
return new Product(name ?? Name, price ?? Price);
}
}
// Then you can write:
var prod2 = prod1.With(name = "New product");
You have to define the method yourself, but that's always the case (unless you're going to use reflection, which less efficient). I think the syntax is reasonably nice too. If you want to make it as nice as in F#, then you'll have to use F# :-)
There is no native ability to do this in C# short of an extension method, but at what cost? a and b are reference types and any suggestion that b is based ("with") on a causes immediate confusion as to how many objects we are working with. Is there only one? Is b a copy of a ? Does b point to a ?
C# is not F#.
Please see a previous SO question of mine as answered by Eric Lippert:
"Amongst my rules of thumb for writing clear code is: put all side effects in statements; non-statement expressions should have no side effects."
More fluent C# / .NET