I'm trying to dynamically get all the values of a specific property from all class instances. I've managed to do it with one property
public class fighter
{
public string Name { get; set; }
public double Height { get; set; }
}
fighter[] roster[5] = new fighter();
string namearray = roster.Select(x => x.Name).ToArray();
int weightarray = roster.Select(x => x.Weight).ToArray();
However I want to reference the property with a variable and put it in a loop so I don't need a select function for every property. Is there anyway to do this, or any other method to get all values of a property from all objects where this could work?
class Program
{
static void Main(string[] args)
{
stud stud = new stud() { id=10,name="test" };
PropertyInfo[] propertyInfo;
propertyInfo = typeof(stud).GetProperties(BindingFlags.Public|BindingFlags.Instance);
foreach (var item in propertyInfo)
{
Console.WriteLine(item.Name + " : " + item.GetValue(stud));
}
Console.ReadLine();
}
}
public class stud {
public int id { get; set; }
public string name { get; set; }
}
use following code, i have created a propertyInfo class from System.Reflection namespace which allows to get property names of the class. after which once you get all the names of properties can be accessed using propertyinfo.GetValue function.
Related
In my DB, all the tables have the status filed, but every table has a different name for that column. For e.g. the user table has user_status and the branch table has branch_status. All these columns of different tables would be having the same value. I have created POCO entities for all and wanted to create a generic function that would perform a query on the status field of the specified POCO entity class. So I wanted to create an attribute stating that this would be the status field of the specified entity class.
So, attribute that will give me the property name on which it is declared. The attribute would be declared only on a single property in the class. Till now I have done the following and it is working but wanted to know the efficient way to achieve the same. Below is my code:
Custom Attribute:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
}
Declaration
public class UserTable
{
public int UserId{ get; set; }
[StatusField]
public int UserStatus { get; set; }
}
public class BranchTable
{
public int BranchId{ get; set; }
[StatusField]
public int BranchStatus { get; set; }
}
I want to get the property name from UserTable and BranchTable having the StatusField Attribute. I have achieve the same as:
Type type = typeof(UserTable);
string statusFieldName = type.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute))).FirstOrDefault().Name;
And the above code gives the proper output as UserStatus. But is there an efficient way to achieve the same using something like below:
[AttributeUsage(AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public class StatusFieldAttribute : Attribute
{
public StatusFieldAttribute([CallerMemberName] string propertyName = null)
{
StatusFieldName = propertyName;
}
public string StatusFieldName { get; }
}
And access the StatusFieldName to get the name of the property on which the StatusField attribute is declared in a particular class.
One approach is to define a marker interface (prior to C# 8), and define an extension method to retrieve the proper status field, like so:
interface IStatusFieldMarker
{
}
static class Extensions
{
public static string GetStatusFieldName(this IStatusFieldMarker t) =>
t.GetType()
.GetProperties()
.Single(p => Attribute.IsDefined(p, typeof(StatusFieldAttributeAttribute)))
.Name;
// optional getter/setter
public static int GetStatus(this IStatusFieldMarker t) =>
(int)(t.GetType()
.GetProperty(GetStatusFieldName(t))
.GetValue(t));
public static void SetStatus(this IStatusFieldMarker t, int value)
{
t.GetType()
.GetProperty(GetStatusFieldName(t))
.SetValue(t, value);
}
}
Then mark the POCO objects with the interface:
class Branch : IStatusFieldMarker
{
[StatusFieldAttribute]
public int BranchStatus { get; set; }
}
class User : IStatusFieldMarker
{
[StatusFieldAttribute]
public int UserStatus { get; set; }
}
You can then call the GetStatusFieldName extension method to get the field name:
Branch b = new Branch() { BranchStatus = 3 };
User u = new User() { UserStatus = 1 };
Console.WriteLine(b.GetStatusFieldName()); // prints BranchStatus
Console.WriteLine(u.GetStatusFieldName()); // prints UserStatus
Console.WriteLine(b.GetStatus()); // prints 3
Console.WriteLine(u.GetStatus()); // prints 1
I simply created below function to get the status name
public static string GetStatusField<T>(this T type) where T : Type
{
PropertyInfo propertyInfo = type.GetProperties().FirstOrDefault(prop => Attribute.IsDefined(prop, typeof(StatusFieldAttribute)));
if (propertyInfo == null)
{
Log.Error($"StatusFieldAttribute not found in {type.Name} ");
throw new Exception($"StatusFieldAttribute not found in { type.Name } ");
}
else
return propertyInfo.Name;
}
And invoked the function as
Type type = typeof(UserTable);
string statusFieldName = type.GetStatusField();
I am having trouble getting the property Names of the IEnumerable properties in my models. I cant seem to get the Nested IEnumerables from the TModel classes. I have looked into some reflection examples but haven't something quite along these lines.
I am looking to just get the IEnumerable property names for each nested model and send the property name to a list. The actual value is not important.
Any help would be greatly appreciated.
// TModel = DataContent in this context.
public class GetModelBase<TModel>
{
public string Error { get; set; }
public IEnumerable<TModel> DataContent { get; set; }
}
public class DataContent
{
public int Total { get; set; }
public IEnumerable<Data> Data { get; set; }
}
public class Data
{
public int DataId{ get; set; }
IEnumerable<DataInformation> DataInformation{ get; set; }
}
public IEnumerable<GetModelBase<TModel>> ResponseAsList<TModel>()
{
// ResponseBody in this context is a string representation of json of the models above...
var toArray = new ConvertJsonArray<GetModelBase<TModel>>(ResponseBody).ReturnJsonArray();
}
// T = GetModelBase<DataContent> in this context.
public class ConvertJsonArray<T>
{
public ConvertJsonArray(string responseString)
{
_responseString = responseString;
Convert();
}
public void Convert()
{
var result = JObject.Parse(_responseString);
// This is where I am having trouble... I am unable to get the nested IEnumerable names.
Type t = typeof(T);
PropertyInfo[] propertyInformation = t.GetProperties(BindingFlags.Public|BindingFlags.Instance);
List<string> toLists = new List<string>();
foreach (PropertyInfo pi in propertyInformation)
toLists.Add(pi.Name);
// End of Property Information Issuse...
foreach (string s in toLists.ToArray())
{
if (result[s] != null)
{
if (!(result[s] is JArray)) result[s] = new JArray(result[s]);
}
}
_jsonAsArray = result.ToString();
}
public string ReturnJsonArray()
{
return _jsonAsArray;
}
private string _responseString { get; set; }
private string _jsonAsArray { get; set; }
}
The result I am looking for in the above code sample would be a list containing only the IEnumerable names as such { "DataContent", "Data", "DataInformation" }
UPDATE:
I am still having trouble looping through each model. I have a nearly working code example.
// This replaces the Type code in the Convert method...
GetProperties(typeof(T))
private void GetProperties(Type classType)
{
foreach (PropertyInfo property in classType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (property.PropertyType.IsGenericType && (property.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
ValuesToList.Add(property.Name);
foreach (Type nestedType in property.PropertyType.GetGenericArguments())
{
GetProperties(nestedType);
}
}
}
}
private List<string> ValuesToList { get; set; }
The results for this yields { "DataContent", "Data" } but fails to get "DataInformation". For some reason the IEnumerables are not hit while in the foreach loop. Additional help would be appreciated.
You already have the PropertyInfo, so you are almost there - all that is left is to recognize which properties are of type IEnumerable<...>, where ... can be an arbitrary type.
For this purpose, check the PropertyType property.
It is a Type instance for which you can check whether it is based upon the generic type definition IEnumerable<T> by means of the GetGenericTypeDefinition method.
That method will throw an exception for non-generic types, so you will also have to check IsGenericType:
if (pi.PropertyType.IsGenericType
&& (pi.PropertyType.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
{
toLists.Add(pi.Name);
}
I got a delegate for OnBuy and OnSell of my items objects, the problem is I'm going to copy paste some of items and not modify the keyword name for each OnBuy and OnSell and try to use "this" keyword, I've added my function to the item class, but still can't access it without modifying the object name after copy paste. Here's my codes:
public static Item item = new Item
{
Name = "Item",
ID = 1,
Price = 50,
Info = "Awesome!",
OnBuy = delegate(Client cli)
{
// Invalid
this.BuyTitle(cli);
// Still can't change
this.Name = "AAA";
return true;
},
OnSell = delegate(Client cli)
{
// Invalid
this.SellTitle(cli);
// Still can't change
this.Name = "AAA";
return true;
}
}
And here's the item class:
public class Item
{
public string Name { get; set; }
public int ID { get; set; }
public int Price { get; set; }
public string Info { get; set; }
public Func<Client, bool> OnBuy { get; set; }
public Func<Client, bool> OnSell { get; set; }
public bool BuyTitle(Client cli)
{
...
}
public bool SellTitle(Client cli)
{
...
}
}
You are using object initializer syntax to create an instance of Item. It is not possible for the anonymous delegate to use this because it is not possible to for an object initializer to reference the object it is creating. From section 7.6.10.2 of the C# spec:
It is not possible for the object initializer to refer to the newly created object it is initializing.
I'm not sure that delegates are the most appropriate mechanism here, but if you still want to use them, I would create a static method which creates the item and call that to initialize the static field.
Here is an outline of what you can do:
public static Item item = CreateItem();
private static Item CreateItem()
{
var item = new Item() { Name = "Item" };
item.OnBuy = client => { item.OnBuy(client); item.Name = "AAA" };
return item;
}
I have two C# classes that have many of the same properties (by name and type). I want to be able to copy all non-null values from an instance of Defect into an instance of DefectViewModel. I was hoping to do it with reflection, using GetType().GetProperties(). I tried the following:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
PropertyInfo[] defectProperties = defect.GetType().GetProperties();
IEnumerable<string> viewModelPropertyNames =
defectViewModel.GetType().GetProperties().Select(property => property.Name);
IEnumerable<PropertyInfo> propertiesToCopy =
defectProperties.Where(defectProperty =>
viewModelPropertyNames.Contains(defectProperty.Name)
);
foreach (PropertyInfo defectProperty in propertiesToCopy)
{
var defectValue = defectProperty.GetValue(defect, null) as string;
if (null == defectValue)
{
continue;
}
// "System.Reflection.TargetException: Object does not match target type":
defectProperty.SetValue(viewModel, defectValue, null);
}
What would be the best way to do this? Should I maintain separate lists of Defect properties and DefectViewModel properties so that I can do viewModelProperty.SetValue(viewModel, defectValue, null)?
Edit: thanks to both Jordão's and Dave's answers, I chose AutoMapper. DefectViewModel is in a WPF application, so I added the following App constructor:
public App()
{
Mapper.CreateMap<Defect, DefectViewModel>()
.ForMember("PropertyOnlyInViewModel", options => options.Ignore())
.ForMember("AnotherPropertyOnlyInViewModel", options => options.Ignore())
.ForAllMembers(memberConfigExpr =>
memberConfigExpr.Condition(resContext =>
resContext.SourceType.Equals(typeof(string)) &&
!resContext.IsSourceValueNull
)
);
}
Then, instead of all that PropertyInfo business, I just have the following line:
var defect = new Defect();
var defectViewModel = new DefectViewModel();
Mapper.Map<Defect, DefectViewModel>(defect, defectViewModel);
Take a look at AutoMapper.
There are frameworks for this, the one I know of is Automapper:
http://automapper.codeplex.com/
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/01/22/automapper-the-object-object-mapper.aspx
Replace your erroneous line with this:
PropertyInfo targetProperty = defectViewModel.GetType().GetProperty(defectProperty.Name);
targetProperty.SetValue(viewModel, defectValue, null);
Your posted code is attempting to set a Defect-tied property on a DefectViewModel object.
In terms of organizing the code, if you don't want an external library like AutoMapper, you can use a mixin-like scheme to separate the code out like this:
class Program {
static void Main(string[] args) {
var d = new Defect() { Category = "bug", Status = "open" };
var m = new DefectViewModel();
m.CopyPropertiesFrom(d);
Console.WriteLine("{0}, {1}", m.Category, m.Status);
}
}
// compositions
class Defect : MPropertyGettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
class DefectViewModel : MPropertySettable {
public string Category { get; set; }
public string Status { get; set; }
// ...
}
// quasi-mixins
public interface MPropertyEnumerable { }
public static class PropertyEnumerable {
public static IEnumerable<string> GetProperties(this MPropertyEnumerable self) {
return self.GetType().GetProperties().Select(property => property.Name);
}
}
public interface MPropertyGettable : MPropertyEnumerable { }
public static class PropertyGettable {
public static object GetValue(this MPropertyGettable self, string name) {
return self.GetType().GetProperty(name).GetValue(self, null);
}
}
public interface MPropertySettable : MPropertyEnumerable { }
public static class PropertySettable {
public static void SetValue<T>(this MPropertySettable self, string name, T value) {
self.GetType().GetProperty(name).SetValue(self, value, null);
}
public static void CopyPropertiesFrom(this MPropertySettable self, MPropertyGettable other) {
self.GetProperties().Intersect(other.GetProperties()).ToList().ForEach(
property => self.SetValue(property, other.GetValue(property)));
}
}
This way, all the code to achieve the property-copying is separate from the classes that use it. You just need to reference the mixins in their interface list.
Note that this is not as robust or flexible as AutoMapper, because you might want to copy properties with different names or just some sub-set of the properties. Or it might downright fail if the properties don't provide the necessary getters or setters or their types differ. But, it still might be enough for your purposes.
This is cheap and easy. It makes use of System.Web.Script.Serialization and some extention methods for ease of use:
public static class JSONExts
{
public static string ToJSON(this object o)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Serialize(o);
}
public static List<T> FromJSONToListOf<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<List<T>>(jsonString);
}
public static T FromJSONTo<T>(this string jsonString)
{
var oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
return oSerializer.Deserialize<T>(jsonString);
}
public static T1 ConvertViaJSON<T1>(this object o)
{
return o.ToJSON().FromJSONTo<T1>();
}
}
Here's some similiar but different classes:
public class Member
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string PetName { get; set; }
public int PetAge { get; set; }
public bool IsUgly { get; set; }
}
public class MemberV2
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsCitizen { get; set; }
public DateTime? Birthday { get; set; }
public string ChildName { get; set; }
public int ChildAge { get; set; }
public bool IsCute { get; set; }
}
And here's the methods in action:
var memberClass1Obj = new Member {
Name = "Steve Smith",
Age = 25,
IsCitizen = true,
Birthday = DateTime.Now.AddYears(-30),
PetName = "Rosco",
PetAge = 4,
IsUgly = true,
};
string br = "<br /><br />";
Response.Write(memberClass1Obj.ToJSON() + br); // just to show the JSON
var memberClass2Obj = memberClass1Obj.ConvertViaJSON<MemberV2>();
Response.Write(memberClass2Obj.ToJSON()); // valid fields are filled
For one thing I would not place that code (somewhere) external but in the constructor of the ViewModel:
class DefectViewModel
{
public DefectViewModel(Defect source) { ... }
}
And if this is the only class (or one of a few) I would not automate it further but write out the property assignments. Automating it looks nice but there may be more exceptions and special cases than you expect.
Any chance you could have both classes implement an interface that defines the shared properties?
Take this class for example:
public class Applicant : UniClass<Applicant>
{
[Key]
public int Id { get; set; }
[Field("X.838.APP.SSN")]
public string SSN { get; set; }
[Field("APP.SORT.LAST.NAME")]
public string FirstName { get; set; }
[Field("APP.SORT.FIRST.NAME")]
public string LastName { get; set; }
[Field("X.838.APP.MOST.RECENT.APPL")]
public int MostRecentApplicationId { get; set; }
}
How would I go about getting all of the properties that are decorated with the field attribute, get their types, and then assign a value to them?
This is all done with reflection. Once you have a Type object, you can then get its PropertyInfo with myType.GetProperties(), from there, you can get each property's attributes with GetCustomAttributes(), and from there if you find your attribute, you've got a winner, and then you can proceed to work with it as you please.
You already have the PropertyInfo object, so you can assign to it with PropertyInfo.SetValue(object target, object value, object[] index)
You'll need to use Reflection:
var props =
from prop in typeof(Applicant).GetProperties()
select new {
Property = prop,
Attrs = prop.GetCustomAttributes(typeof(FieldAttribute), false).Cast<FieldAttribute>()
} into propAndAttr
where propAndAttr.Attrs.Any()
select propAndAttr;
You can then iterate through this query to set the values:
foreach (var prop in props) {
var propType = prop.Property.PropertyType;
var valueToSet = GetAValueToSet(); // here's where you do whatever you need to do to determine the value that gets set
prop.Property.SetValue(applicantInstance, valueToSet, null);
}
You would just need to invoke the appropriate reflection methods - try this:
<MyApplicationInstance>.GetType().GetProperties().Where(x => x.GetCustomAttributes().Where(y => (y as FieldAttribute) != null).Count() > 0);