I have a set of messages which I cannot alter their structure, but all of them follow the same property structure after you drill into the first two. For example,
public class Animal {
public Dog Doggy { get; set; }
}
public class MixedAnimal {
public CatDog CatDoggy { get; set; }
}
public class Dog {
public Name name { get; set; }
public Age age { get; set; }
}
public class CatDog {
public Name name { get; set; }
public Age age { get; set; }
}
If I have a structure like this: SomeObj.Item where SomeObj is some object and Item is of type object which can hold either an Animal or MixedAnimal. How would I get to the value of either Dog or CatDog using the keyword dynamic?
I can get the top level object using SomeObj.Item as dynamic, and then do:
(SomeObj.Item as dynamic).Doggy.Name
but what I want is to just get the name without knowing the type of Item.
(SomeObj.Item as dynamic).(Something as dynamic).Name
Is this possible?
Using reflection is quite easy to resolve this problem. Something like this (general idea):
object animal = new Animal { Doggy = new Dog { age = 10, name = "Good boy" }};
var members = animal.GetType().GetMembers();
foreach (PropertyInfo member in members.Where(x => x is PropertyInfo))
{
if (member.PropertyType.Name == "Dog" || member.PropertyType.Name == "CatDog")
{
var propertyValue = member.GetValue(animal);
var propertyType = propertyValue.GetType();
var nameMember = propertyType.GetProperty("name");
var ageMember = propertyType.GetProperty("age");
var nameValue = nameMember.GetValue(propertyValue);
var ageValue = ageMember.GetValue(propertyValue);
Console.WriteLine($"Name: {nameValue}, Age: {ageValue}");
}
}
Everything you need to do additionally is providing list of type names which you want to process (like "Dog" or "CatDog" here).
Related
Consider the following class -
public class User
{
[Selected]
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
[Selected]
public int Code { get; set; }
public object GetValue()
{
// need to do something here
}
}
[Selected] here is nothing but a marker attribute. I want GetValue method to return an object which will have the [Selected]-marked properties with corresponding values. That is, in the code below -
private static void Execute()
{
User user = new User
{
Name = "alice",
Email = "alice#liddell.com",
Password = "123456",
Code = 1234
};
var value = user.GetValue();
}
value should be an object with two properties Name and Code which should have the values "alice" and 1234 respectively.
After some searching I tried ExpandoObject (which I never used before) -
public object GetValue()
{
var dictionary = this.GetType().GetProperties().Where(p => p.GetCustomAttribute(typeof(Selected), false) != null).ToDictionary(p => p.Name);
dynamic expando = new ExpandoObject();
foreach (var item in dictionary)
{
object value = item.Value.GetValue(this);
((IDictionary<string, object>)expando).Add(item.Key, value);
}
return expando;
}
But it didn't serve my purpose - the client/consumer of value object somehow couldn't read/access the property values.
Any suggestions?
Edit :
There might be a lot of classes like User and the GetValue method will be called from within a generic method. So, at runtime I have no way to know what type the object is and which properties are marked.
To access the fields by name it is easier to cast the returned object to IDictionary:
var value = (IDictionary<string, object>) user.GetValue();
Console.WriteLine(value["Name"]);
Simplify your method to this:
public Dictionary<string, object> GetValue()
{
var dictionary = this.GetType()
.GetProperties()
.Where(p => p.GetCustomAttribute(typeof(Selected), false) != null)
.ToDictionary(p => p.Name, p => p.GetValue(this));
return dictionary;
}
Use:
var value = user.GetValue(); // value is Dictionary
foreach (var kvp in value)
{
Console.WriteLine(kvp);
}
If you wish POCO, then you can do it like follows
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public int Code { get; set; }
public SelectedUserProperties GetValue()
{
return new SelectedUserProperties
{
Name = Name,
Code = Code
};
}
}
public class SelectedUserProperties
{
public string Name { get; set; }
public int Code { get; set; }
}
It is assumed that the selected properties are known in advance, before compilation.
This makes the marker attribute unnecessary and can be completely removed.
What you did works well, you just have to use the keyword dynamic when you call the method GetValue.
dynamic value = user.GetValue();
If you are using var, value will be of the same type as the return type of your function (i.e object) at compile time. Therefore, if you try to do value.Name your compiler won't allow it because the class Object doesn't have any attribute Name.
dynamic tells your program to do the type checking at runtime.
I have a bunch of classes generated by EF that are simple tables and have similar structures:
public class Contact
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member
{
public int ID { get; set; }
public string Description { get; set; }
}
I've also got a method for returning an object of a specified type:
public T GetInstance<T>(string type)
{
return (T)Activator.CreateInstance(Type.GetType(type));
}
What I want to do is something like this:
public ActionResult GetAll(string ClassType) // ClassType will be the name of one of the classes above
{
Object LookupType = GetInstance<Object>(ClassType);
List<LookupType> AllList = new List<LookupType>();
AllList = repo.GetAll<LookupType>().ToList<LookupType>(); // some generic method that will return a list;
}
This makes the compiler mad because I'm using a variable (LookupType) rather than a true type to build the list. However, neither of these work either:
List<LookupType.GetType()> Items = new List<LookupType.GetType()>();
List<typeof(LookupType)> Items = new List<typeof(LookupType)>();
Both cause an error - "Using generic type List requires 1 type argument".
Is there a proper way to do this? Is there a way to convert ClassType directly to a type without first making it an object (from which I hope to derive the type)?
Try using the CreateInstance method
SomeObject someObject = new SomeObject();
Type type = someObject.GetType();
Type listType = typeof(List<>).MakeGenericType(new [] { type });
IList list = (IList)Activator.CreateInstance(listType);
You cannot do it with C#!!
Compiler must to know the parameter type.
In that maybe you would like to accomplish, interfaces will help you
public class Contact: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public class Member: IIdDescription
{
public int ID { get; set; }
public string Description { get; set; }
}
public interface IIdDescription
{
int ID { get; set; }
string Description { get; set; }
}
public ActionResult GetAll(string type)
{
var AllList = new List<IIdDescription>();
if(type == Member.GetType().Name)
AllList = repo.Set<Member>().Cast<IIdDescription >().ToList();
if(type == Contact.GetType().Name)
AllList = repo.Set<Contact>().Cast<IIdDescription >().ToList();
...
}
and into your view use interface as model, something like
#model IEnumerable<IIdDescription>
If you don't know the type ahead of time maybe using a list of dynamic objects can help.
var item = GetInstance<Contact>("Namespace.Contact");
var items = new List<dynamic>();
items.Add(item);
You can then access the types like so...
Contact contact = items[0];
Just be aware that using dynamic can be expensive.
I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.
I have a first list of entities like this :
public partial class Networking :EntityBase
{
public virtual int NetWorkingId
{
get;
set;
}
public virtual string NetWorkingParam
{
get;
set;
}
public virtual System.DateTime NetWorkingDate
{
get;
set;
}
}
And I have a second list of entities like this:
public partial class PrivateNetwork :EntityBase
{
public virtual int PrivateNetworkId
{
get;
set;
}
public virtual int ContaId
{
get { return _contaId; }
set
{
if (_contaId != value)
{
if (Contact != null && Contact.ContaId != value)
{
Contact = null;
}
_contaId = value;
}
}
}
public virtual Nullable<System.DateTime> DateCreation
{
get;
set;
}
}
I want to collect these two lists in one and sort all the elements by date.
Is that possible ?
You can do this, although it's not very pretty, and you end up with an IEnumerable<object> so you have to check each item's type before you can use it:
IEnumerable<object> sorted = myNetworkingList
.Concat<object>(myPrivateNetworkList)
.OrderBy(n => n is Networking
? (DateTime?)((Networking)n).NetWorkingDate
: ((PrivateNetwork)n).DateCreation);
foreach (object either in sorted)
{
if (either is Networking)
// Networking; do something
else
// PrivateNetwork; do something else
}
This problem could easily be solved by using polymorphism; use a common base class or interface for both classes, which has the DateTime property you want to sort on.
Example:
public abstract class NetworkingBase : EntityBase
{
public DateTime DateToSortOn { get; set; }
}
or
public interface INetworking
{
public DateTime DateToSortOn { get; set; }
}
And then make your classes derive from NetworkingBase or implement INetworking:
public partial class Networking : NetworkingBase
{
...
}
public partial class PrivateNetwork : NetworkingBase
{
...
}
or
public partial class Networking : EntityBase, INetworking
{
...
}
public partial class PrivateNetwork : EntityBase, INetworking
{
...
}
Do a LINQ Union or Concat and then an OrderBy on the resulting collection.
What I should have asked earlier is . . .
What do you want to do after you've sorted them?
The answer to this could have a big impact on the potential solution.
If the answer is something like I need to display a list of the dates, where you only need the dates in order. If so then you don't need to merge the two lists, you can get a sequence of just the ordered dates and use that e.g.
var orderedDates = networks.Select(n => n.NetworkingDate)
.Union(privateNetworks.Select(n => n.DateCreation))
.OrderBy(date => date);
If the answer is I need to display a list of links showing the Date that links to the Id of the object, and something to identify the type of object, then you could get away with something very like the above, with an Anonymous object.
var orderedDates = networks.Select(n => new {Date = n.NetworkingDate, Id = n.NetWorkingId, NetworkType = n.GetType().Name})
.Union(privateNetworks.Select(n => new {Date = n.DateCreation, Id = n.PrivateNetWorkingId, NetworkType = n.GetType().Name}))
.OrderBy(n => n.Date);
However if the answer is I need to send a Shutdown() command to the 10 oldest networks then you really do need a polymorphic solution, where you have a single type that you can call a Shutdown() method on, that will resolve to the specific Shutdown() method on the types you're using.
A Polymorphic solution to use only if user khellang's answer doesn't work for you
From a comment on another answer
#BinaryWorrier I chose this answer because I already have records in
the database, so if I choose to add a new interface how will I deal
with the records already stored before adding the interface ?
I find it difficult to believe that your ORM won't allow you to add an interface to an entity class and not - somehow - mark that interface and/or it's member so they're ignored by the ORM.
However, assuming you can't add a new interface or base class, you can still do this polymorphically.
Add the interface, add a class implementing the interface that for each of your Network classes (the Abstractor classes), then transform the network classes into Abstractor classes, adding them to a List<INetwork> and sorting that list.
public interface INetwork
{
DateTime? Date { get; }
}
public class PrivateNetworkAbstractor
:INetwork
{
private PrivateNetwork network;
public PrivateNetworkAbstractor(PrivateNetwork network)
{
this.network = network;
}
public DateTime? Date
{
get { return network.DateCreation; }
}
}
public class NetworkingAbstractor
: INetwork
{
private Networking networking;
public NetworkingAbstractor(Networking networking)
{
this.networking = networking;
}
public DateTime? Date
{
get { return networking.NetWorkingDate; }
}
}
...
public IEnumerable<INetwork> MergenSort(IEnumerable<Networking> generalNetWorks, IEnumerable<PrivateNetwork> privateNetWorks)
{
return generalNetWorks.Select(n => new NetworkingAbstractor(n)).Cast<INetwork>()
.Union(privateNetWorks.Select(n => new PrivateNetworkAbstractor(n)).Cast<INetwork>())
.OrderBy(n=> n.Date);
}
Create an interface that has the date and implement in in both classes. After that sorting is easy.
public interface INetwork
{
DateTime? Date { get; }
}
public partial class Networking :EntityBase, INetwork
{
public DateTime? Date
{
get { return NetWorkingDate; }
}
}
public partial class PrivateNetwork :EntityBase, INetwork
{
public DateTime? Date
{
get { return DateCreation; }
}
}
var commonList = new List<INetwork>();
// Add instances of PrivateNetwork and Networking to the list
var orderedByDate = commonList.OrderBy(n => n.Date);
First solution is using anonymous type
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Example1
{
class Program
{
class Human
{
public string Name { get; set; }
public string Hobby { get; set; }
public DateTime DateOfBirth { get; set; }
}
class Animal
{
public string Name { get; set; }
public string FavouriteFood { get; set; }
public DateTime DateOfBirth { get; set; }
}
static void Main(string[] args)
{
var humans = new List<Human>
{
new Human
{
Name = "Kate",
Hobby = "Fitness",
DateOfBirth = DateTime.Now.AddYears(-27),
},
new Human
{
Name = "John",
Hobby = "Cars",
DateOfBirth = DateTime.Now.AddYears(-32),
},
};
var animals = new List<Animal>
{
new Animal
{
Name = "Fluffy",
FavouriteFood = "Grain",
DateOfBirth = DateTime.Now.AddYears(-2),
},
new Animal
{
Name = "Bongo",
FavouriteFood = "Beef",
DateOfBirth = DateTime.Now.AddYears(-6),
},
};
var customCollection = (from human in humans
select new
{
Name = human.Name,
Date = human.DateOfBirth,
}
).Union(from animal in animals
select new
{
Name = animal.Name,
Date = animal.DateOfBirth,
}).OrderBy(x => x.Date);
foreach (dynamic customItem in customCollection)
Console.WriteLine(String.Format("Date: {0}, Name: {1}", customItem.Date, customItem.Name));
Console.Read();
}
}
}
or without anonymous type (created CustomClass):
...
class CustomClass
{
public string Name { get; set; }
public DateTime Date { get; set; }
}
...
var customCollection = (from human in humans
select new CustomClass
{
Name = human.Name,
Date = human.DateOfBirth,
}
).Union(from animal in animals
select new CustomClass
{
Name = animal.Name,
Date = animal.DateOfBirth,
}).OrderBy(x => x.Date);
foreach (CustomClass customItem in customCollection)
Console.WriteLine(String.Format("Date: {0}, Name: {1}", customItem.Date, customItem.Name));
...
I simply added a base class and assigned it as the parent of both list's classes . and then simple did the union . it made the trick
I have a class
public class ProjectTask
{
public ProjectTask();
[XmlElement("task_creator_id")]
public string task_creator_id { get; set; }
[XmlElement("task_owner_id")]
public string task_owner_id { get; set; }
[XmlElement("task_owner_location")]
public TaskOwnerLocation task_owner_location { get; set; }
[XmlElement("task_owner_type")]
public string task_owner_type { get; set; }
[XmlElement("task_type_description")]
public string task_type_description { get; set; }
[XmlElement("task_type_id")]
public string task_type_id { get; set; }
[XmlElement("task_type_name")]
public string task_type_name { get; set; }
}
An xml will be deserialized to this at runtime.
Is there way to get the field name and value?
Using reflection I can get the property names like so:
PropertyInfo[] projectAttributes = typeof(ProjectTask).GetProperties();
A foreach loop can be applied to get the properties
foreach(PropertyInfo taskName in projectAttributes)
{
Console.WriteLine(taskName.Name);
}
but how do I print the property and the value?
Like
task_creator_id = 1
where task_Id is one of the properties and the value of that at runtime is 1.
Use taskName.GetValue(yourObject,null)
where yourObject should be of an instance of ProjectTask. For ex,
ProjectTask yourObject = (ProjectTask)xmlSerializer.Deserialize(stream)
var propDict = typeof(ProjectTask)
.GetProperties()
.ToDictionary(p => p.Name, p => p.GetValue(yourObject, null));
You can do that using your PropertyInfo object :
var propertyName = MyPropertyInfoObject.Name;
var propertyValue = MyPropertyInfoObject.GetValue(myObject, null);
A foreach loop give you access to all properties of your type, you can also have a specefic property knowing its name, like so :
var MyPropertyInfoObject = myType.GetProperty("propertyName");