I want to make a generic search UserControl in wpf. I want it to get a collection of objects and a name of a property to search by.
The problem is I can't use generics because the code that calls the search function also can't know of the type.
Is there a way to achieve this? Or some way to query an Object that is underneath another type?
Consider this example.
interface IFoo
{
}
class Bar1 : IFoo
{
//interface implementations
public string Property1 { get; set; }
public string myProperty1 { set; get; }
}
class Bar2 : IFoo
{
//interface implementations
public string Property1 { get; set; }
public string myProperty1 { set; get; }
}
//Search the list of objects and access the original values.
List<IFoo> foos = new List<IFoo>();
foos.Add(new Bar1
{
Property1 = "bar1",
myProperty1 ="myBar1"
});
foos.Add(new Bar1());
foos.Add(new Bar2());
foos.Add(new Bar2());
//Get the objects.
foreach (var foo in foos)
{
//you can access foo directly without knowing the original class.
var fooProperty = foo.Property1;
//you have to use reflection to get the original type and its properties and methods
Type type = foo.GetType();
foreach (var propertyInfo in type.GetProperties())
{
var propName = propertyInfo.Name;
var propValue = propertyInfo.GetValue(foo);
}
}
var result = list.Where(a => a.propertyName);
You can use reflection
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
var Data = new List<object>() { new A() { MyProperty = "abc" }, new B() { MyProperty = "cde"} };
var Result = Data.Where(d => (d.GetType().GetProperty("MyProperty").GetValue(d) as string).Equals("abc"));
// Result is IEnumerable<object> wich contains one A class object ;)
}
}
class A
{
public string MyProperty { get; set; }
}
class B
{
public string MyProperty { get; set; }
}
}
Related
I have a generic method that can be called with 2 different object types, TypeA or TypeB. TypeA and TypeB are essentially identical classes except in name only. I am trying to determine how to prevent from having to duplicate the Foreach loop code for each object type. Is this possible ? thanks.
public class TypeA
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
public class TypeB
{
public string Name { get; set; }
public string Department { get; set; }
public string Total { get; set; }
}
private CsvExport GenerateExport<T>(IEnumerable<T> scores)
{
CsvExport export = new CsvExport();
List<TypeA> aList = null;
List<TypeB> bList = null;
Type type = scores.GetType();
if (type.FullName.Contains("TypeA"))
{
aList = scores as List<ObjectaModel>;
}
else if (type.FullName.Contains("TypeB"))
{
bList = scores as List<ObjectbModel>;
}
foreach (var dt in aList)
{
export.AddRow();
export["Name"] = dt.Name;
export["Department"] = dt.Department;
export["Total "] = dt.Total;
};
return export;
}
In this particular case I strongly suggest you delegate the hard work to the CsvHelper library which you can also obtain from Nuget and is used like this...
public void ExportToCsv<T>(string filename, ImmutableArray<T> objects)
{
using (var writer = File.CreateText(filename))
{
var csv = new CsvWriter(writer);
csv.WriteRecords(objects);
}
}
The more general answer to your question is that you must either have both classes inherit from a common class or interface or you would have to use reflection to look for an obtain the values of the named properties.
Using a common interface...
public interface IScore
{
int HiScore {get;}
}
public class ScrabbleScore : IScore
{
public int HiScore {get;set;}
}
public class PacManScore : IScore
{
public int HiScore {get;set;}
}
public void Export<T>(IEnumerable<T> scores) where T: IScore
{
foreach(var s in scores)
{
CvsExport["Hi score"]= s.HiScore;
}
}
Using reflection...
var CsvExport = new Dictionary<string,string>();
foreach(var o in scores)
{
//note that checking the type for each object enables you to have heterogenous lists if you want
var objectType= o.GetType();
foreach(var p in objectType.GetProperties())
{
var propertyName = p.Name;
CsvExport[propertyName] = p.GetValue(o).ToString();
}
}
I would treat the reflection solution as the least favoured of the three.
I have the following class example here:
public class Foo
{
public int Id { get; set; }
public string Bar { get; set; }
public string FooBar { get; set; }
public string Fizz { get; set; }
public string Buzz { get; set; }
public static Foo Create(int id, string property, string value)
{
return new Foo
{
Id = id,
};
}
}
Now, however, i want to set for example only Bar to a value in the Create method of the class if the propertyname is Bar . So i took a look at C# setting/getting the class properties by string name but i can't get this to work in the create method. Same goes for Setting a property by reflection with a string value so i'm kinda lost here.
As a small bit of clarification. I can get the above methods to work in the create, however not in the return which is where it needs to work.
The solution below seems to work for us for now, however, i think it could be optimized.
public class Foo
{
public int Id { get; set; }
public string Bar { get; set; }
public string FooBar { get; set; }
public string Fizz { get; set; }
public string Buzz { get; set; }
public static Foo Create(int id, string property, string value)
{
return new Foo
{
WebshopCustomerId = webshopCustomerId,
Bar = (typeof(Foo)).GetProperty(property).Name == "Bar" ? value : null,
FooBar = (typeof(Foo)).GetProperty(property).Name == "FooBar" ? value : null,
Fizz = (typeof(Foo)).GetProperty(property).Name == "Fizz" ? value : null,
Buzz = (typeof(Foo)).GetProperty(property).Name == "Buzz" ? value : null,
};
}
}
You can avoid reflection and keep everything simpler using a dictionary of Actions:
public class Foo
{
public int Id { get; set; }
public string Bar { get; set; }
public string FooBar { get; set; }
public string Fizz { get; set; }
public string Buzz { get; set; }
private static Dictionary<string, Action<Foo, string>> _propertySetters =
new Dictionary<string, Action<Foo, string>>()
{
{ "Bar", (foo, value) => foo.Bar = value },
{ "FooBar", (foo, value) => foo.FooBar = value },
{ "Fizz", (foo, value) => foo.Fizz = value },
{ "Buzz", (foo, value) => foo.Buzz = value },
};
public static Foo CreateWithProperty(int id, string property, string value)
{
if (String.IsNullOrEmpty(property) || !_propertySetters.ContainsKey(property))
throw new ArgumentException("property");
var instance = new Foo { Id = id };
var setter = _propertySetters[property];
setter(instance, value);
return instance;
}
}
With this approach, you can even change the property names, while keeping the configuration values the same. Depending upon the situation, it could be a huge advantage.
Having said this, I feel that your requirement could probably be better answered with more context information and a slightly different design. YMMV.
public static Foo Create(int id, string property, string value)
{
Foo ret = new Foo
{
Id = id
};
foreach (FieldInfo element in typeof(Foo).GetFields())
if (element.Name == property)
element.SetValue(ret, value);
foreach (PropertyInfo element in typeof(Foo).GetProperties())
if (element.Name == property)
element.SetValue(ret, value);
return ret;
}
Something looking like this should work for you, you could also use
ret.GetType().GetProperty(property).SetValue(ret, value);
ret.GetType().GetField(property).SetValue(ret, value);
But you'll have to handle errors.
It will work for you, but you still need to check if property is a valid property.
public static Foo Create(int id, string property, string value)
{
Foo foo = new Foo() { Id = id };
PropertyInfo propertyInfo = foo.GetType().GetProperty(property);
propertyInfo.SetValue(foo, Convert.ChangeType(value, propertyInfo.PropertyType), null);
return foo;
}
I have a following LINQ expression:
var query = entities
.Select(e => new MyObject()
{
Property1 = e.Item1,
Property2 = e.Item2
});
MyObject might have also Property3, Property4 defined. I need to realize which properties are part of LINQ projection via expression visitor.
So I call something like:
var listOfProperties = query.GetSelectedPropertyNames();
and the content of listOfProperties will be string array which contains Property1, Property2 or something by which I can check:
var isPropertyInProjection = query.HasPropertyInProjection(nameof(MyObject.Property3));
and the result will be false.
You can easily do that using an ExpressionVisitor. Just create a new class and override the visiting methods. If you know that the projection was done using member bindings, you can simply override the method VisitMemberBinding and add the bound member to a list that you store as an instance variable. Then all you need to do is to make that instance variable public.
class ProjectionAnalyzer : ExpressionVisitor
{
private HashSet<MemberInfo> boundMembers = new HashSet<MemberInfo>();
protected override MemberBinding VisitMemberBinding(MemberBinding node)
{
boundMembers.Add(node.Member);
return base.VisitMemberBinding(node);
}
public IEnumerable<MemberInfo> BoundMembers => boundMembers;
}
Then, use this class as follows:
var analyzer = new ProjectionAnalyzer();
analyzer.Visit(selectorPredicate);
var boundMembers = analyzer.BoundMembers;
How you obtain the selector predicate depends on your LINQ provider.
I did something similar using VisitMemberAssignment:
namespace BoundPropertiesinQuery
{
static class IEnumerableExtensions
{
class ProjectedVisitor : ExpressionVisitor
{
public IList<string> ProjectedPropertyNames { get; set; } = new List<string>();
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
ProjectedPropertyNames.Add(node.Member.Name);
return base.VisitMemberAssignment(node);
}
}
public static IEnumerable<string> ProjectedProperties<T>(this IQueryable<T> #this)
{
var pv = new ProjectedVisitor();
pv.Visit(#this.Expression);
return pv.ProjectedPropertyNames.Distinct();
}
}
internal class MyObject
{
public int Property1 { get; set; }
public int Property2 { get; set; }
public int Property3 { get; set; }
public int Property4 { get; set; }
}
internal class MyOtherObject
{
public int other1 { get; set; }
public int other2 { get; set; }
public int other3 { get; set; }
public int other4 { get; set; }
}
internal class Program
{
private static void Main(string[] args)
{
var listOfItems = new List<MyOtherObject>()
{
new MyOtherObject
{
other1 = 1,
other2 = 2,
other3 = 3,
other4 = 4
},
new MyOtherObject
{
other1 = 5,
other2 = 6,
other3 = 7,
other4 = 8
}
};
var result = listOfItems.AsQueryable().Select(m => new MyObject
{
Property1 = m.other1,
Property2 = m.other2
}).ProjectedProperties();
foreach (var item in result)
{
Console.WriteLine(item);
}
Console.ReadLine();
}
}
}
I'm trying to create a custom dictionary that uses a List as a base (will be used in XML deserialization). I can't figure out how to create it as the key can't be put on string it seems.
The dictionary would have the key property of the TestObject as the Key and as Value it would have the TestObject itself.
public class TestObject
{
public string Key { get; set; }
public string Property1 { get; set; }
public int Property2 { get; set; }
}
public class CustomDictionary<string, TestObject> : List<TestObject>
{
}
public class Methods
{
public void TestMethod(List<TestObject> list)
{
var testObject = new TestObject()
{
Key = "TEST",
Property1 = "ABC",
Property2 = 123,
};
CustomDictionary<string, TestObject> dictionary = new CustomDictionary<string, TestObject>(list);
var test;
dictionary.TryGetValue(testObject.Key, out test);
}
}
Given your last comment, what you want to do is something like this:
public class Methods
{
public void TestMethod(List<TestObject> list)
{
Dictionary<string, TestObject> data = list.ToDictionary(x => x.Key);
}
}
This uses LINQ's ToDictionary method, although a simple foreach loop would suffice.
I want to define an extension method of List<T> called MergeAll(). I want it to take in elements from a list of one type and produce a list of another. I have a delegate defined for the Merger (equivalent of Converter)
public delegate TOutput Merger<in TInput, out TOutput>(TInput input)
but cannot for the life of me figure out the syntax of the extension method. My attempt is:
public static List<TOutput> MergeAll<TOutput>(this List<TOutput> output,
Merger<TOutput, TInput> merger)
Then, what should the body of MergeAll look like?
What you're attempting is called a projection.
An extension method is already included in the .NET Framework to achieve this. IEnumerable.Select, and you can use it in the fashion below.
void Main()
{
List<Foo> foos = new List<Foo>
{
new Foo { Name = "Fu" },
new Foo { Name = "Foe" },
new Foo { Name = "Thumb" }
};
IEnumerable<Bar> bars = foos.Select(foo => new Bar
{
BarId = foo.Id,
Name = foo.Name
});
}
public class Foo
{
public Foo() { Id = Guid.NewGuid().ToString(); }
public string Id { get; set; }
public string Name { get; set; }
}
public class Bar
{
public Bar()
{
this.BarId = Guid.NewGuid().ToString();
this.TimeCreated = DateTime.UtcNow;
}
public string BarId { get; set; }
public string Name { get; set; }
public DateTime TimeCreated { get; set; }
}
How it's implemented....
If you wanted to implement a custom solution yourself for the sake of learning, this is how you would go about doing it:
public static class Extensions
{
public static IEnumerable<TDestination> ConvertTo<TFrom, TDestination>(this IEnumerable<TFrom> fromCollection, Func<TFrom, TDestination> expression)
{
List<TDestination> destinationList = new List<TDestination>();
foreach (var element in fromCollection)
{
destinationList.Add(expression.Invoke(element));
}
return destinationList;
}
}
void Main()
{
List<Foo> foos = new List<Foo>
{
new Foo { Name = "Fu" },
new Foo { Name = "Foe" },
new Foo { Name = "Thumb" }
};
IEnumerable<Bar> customBars = foos.ConvertTo(foo => new Bar
{
BarId = foo.Id,
Name = foo.Name
});
}
You need to add TInput to MergeAll<TInput, TOutput> and to change the first parameter to be List<TInput> and the second to be Func<TInput, TOutput>.
public static List<TOutput> MergeAll<TInput, TOutput>(this List<TInput> inputs,
Func<TInput, TOutput> merger)
{
var outputs = new List<TOutput>();
foreach (var input in inputs)
{
outputs.Add(merger(input));
}
return outputs;
}
A simple usage that convert double to int will look like below:
List<double> doubles = new List<double> { 1.3, 2.2, 3.5, 4.7 };
List<int> ints = doubles.MergeAll(doubleParam => Convert.ToInt32(doubleParam)).ToList();