Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I have the following classes
public class Car
{
public int CarId { get; set;}
public string Description { get; set;}
public Engine Engine { get; set;}
}
public class Engine
{
public int EngineId { get; set;}
public int Description { get; set;}
}
Now i want to iterate all the properties in Car and all the properties in Engine, i dont want to hardcode the property names "Car or Engine"
Example to get all properties of Car, where obj is instance of Car.
var properties = obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
But this doesnt iterate the properties of Engine.
FlattenHierarchy does not do what you think it does, and instead follows the inheritance hiearchy for static members.
If you'd like to get sub-properties for objects, you'll need to do that yourself:
static IEnumerable<PropertyInfo> FlattenProperties(Type type)
{
// Assumption #1: you do not want "simple" types enumerated
if (!type.IsClass)
return Enumerable.Empty<PropertyInfo>();
// Assumption #2: you want to ignore "the usual suspects"
if (type.Namespace != null && type.Namespace.StartsWith("System"))
return Enumerable.Empty<PropertyInfo>();
// Assumption #3: your class hierarchy won't destroy recursion
// Assumption #4: you simply want the PropertyInfo
return type.GetProperties(BindingFlags.FlattenHierarchy
| BindingFlags.Instance
| BindingFlags.Public)
.SelectMany(pi => new[] { pi }
.Concat(FlattenProperties(pi.PropertyType)));
}
If this is used in code where you (a) know the depth of the recursion, and (b) have the means to alter the code, I'd suggest creating either a base class, interface, or attribute for these types/properties.
// Replace Assumptions #1 and #2 above with this:
// Assumption #5: given interface ISomething { }
if (!typeof(ISomething).IsAssignableFrom(type))
return Enumerable.Empty<PropertyInfo>();
If you need the "property tree" (i.e. Assumption #4 is incorrect):
static IEnumerable<IEnumerable<PropertyInfo>> FlattenProperties(
Type type,
IEnumerable<PropertyInfo> ancestors = null)
{
// change to Assumptions #1/#2 or #5 to yield break
// ...
ancestors = ancestors ?? Enumerable.Empty<PropertyInfo>();
var properties = type.GetProperties(
BindingFlags.FlattenHierarchy
| BindingFlags.Instance
| BindingFlags.Public);
foreach (var property in properties)
{
// again, Assumption #3: your class hierarchy won't destroy recursion
// Assumption #6: you actually want the initial nested property too
yield return ancestors.Concat(new[] { property });
foreach (var nested in FlattenProperties(
property.PropertyType,
ancestors.Concat(new [] { property })))
{
yield return nested;
}
}
}
Which, in the second case, produces output similar to:
// foreach (var tree in FlattenProperties(typeof(Car)))
// {
// Console.WriteLine("{0}", String.Join(".", tree.Select(pi => pi.Name)));
// }
CarId
Description
Engine
Engine.EngineId
Engine.Description
Try this. You've to repeat the same for all properties of a type to get nested properties.
var properties = obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
foreach(var pi in properties)
{
var nestedProperties = pi.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public);
}
You can just iterate through the properties of each property's PropertyType, just as you've done to get the first-level properties.
Here's a quick and dirty example using Linq:
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
select new { OuterProperty = p1, InnerProperty = p2 };
foreach(var prop in properties)
{
Console.WriteLine(prop.OuterProperty.Name + (prop.InnerProperty != null ? "." + prop.InnerProperty.Name : ""));
}
Produces the output:
CarId
Description.Chars
Description.Length
Engine.EngineId
Engine.Description
You might want to only evaluate classes in a given namespace (so you don't end up capturing the Length property of Description, for example):
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.PropertyType.Namespace == "MyNamespace"
? p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
: new PropertyInfo[] { null }
select new { OuterProperty = p1, InnerProperty = p2 };
Produces the output:
CarId
Description
Engine.EngineId
Engine.Description
Or perhaps more elegantly if you define a certain attribute to mark the properties you want to traverse:
[AttributeUsage(AttributeTargets.Property)]
public class TraversableAttribute: Attribute { }
public class Car
{
public int CarId { get; set;}
public string Description { get; set;}
[Traversable]
public Engine Engine { get; set;}
}
...
var properties =
from p1 in obj.GetType().GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
from p2 in p1.GetCustomAttributes(typeof(TraversableAttribute), true).Length > 0
? p1.PropertyType.GetProperties(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public).DefaultIfEmpty()
: new PropertyInfo[] { null }
select new { OuterProperty = p1, InnerProperty = p2 };
This will produce the same output as the previous example.
If you only need the properties from the original class and its 1st lower hierarchy level, then you could just create the following attribute like so.
public class BrowsableAttribute : Attribute { }
Now simply decorate the classes you want to browse for properties, in your case, that would be the Engine class.
[Browsable]
class Engine
{
public int EngineId { get; set; }
public int Description { get; set; }
}
Now all you need to do is use the following extension method.
public static class TypeExtensions
{
public static void BrowseProperties(this Type type)
{
var h1 = typeof(Car).GetProperties().ToList();
var h2 = h1.Where(x => x.PropertyType.GetCustomAttributes(true).OfType<BrowsableAttribute>().Any());
h2.ToList().ForEach(x => h1.AddRange(x.PropertyType.GetProperties()));
h1.ForEach(x => Console.WriteLine(x.Name));
}
}
Which will produce the desired result. To see this, execute the following lines of code.
class Program
{
static void Main()
{
typeof(Engine).BrowseProperties();
Console.Read();
}
}
Related
I am wondering if something like this is possible. I am looking to create a method that can be called instead of List.Add. The method would check any/all string properties and make sure they don't exceed their specific given max lengths, and if so truncate to proper size. I would ideally like it to be generic so that it will not only work for ObjectA, but also ObjectB, ObjectC, etc.
I am open to any and all suggestions. I know it seems like a weird thing to do, but I have a lot of different objects I am working with and potentially millions of those object instances in totality across all my lists. I mainly just need a way to ensure that any objects with properties exceeding their max string limit are truncated and logged via the Worker class in a timely way. Thanks!
public class ObjectA {
public Guid aID {get; set;}
[MaxLength(128)]
public string aName {get; set;}
[MaxLegnth(30)]
public string aType {get; set;}
}
--
public class Worker {
private void Work() {
List<ObjectA> listOfA = new List<ObjectA>();
listOfA.CustomAddMethod(new ObjectA(new Guid, "Something", "Unknown"));
}
// ??????
private CustomAddMethod(T object) {
foreach property {
if (isStringProperty && isGreaterThanMaxLength) {
// truncate to proper size
// log for truncation message
}
// Then add to list
}
}
}
You can create an extension method.
Here is a code snip. You can improve the performance by implementing a cache, for example, using a dictionary to store the properties and the MaxLengthAttribute based on object's type.
public static class ListExtensions
{
public static void CustomAdd<T>(this List<T> list, T item, Action<string> logger = null)
{
var propertyInfos = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(propertyInfo => propertyInfo.PropertyType == typeof(string))
.ToList();
foreach (var propInfo in propertyInfos)
{
var maxLengthAttr = propInfo
.GetCustomAttributes(typeof(MaxLengthAttribute))
.Cast<MaxLengthAttribute>()
.FirstOrDefault();
if (maxLengthAttr is null)
continue;
var currentString = (string)propInfo.GetValue(item);
if (!maxLengthAttr.IsValid(currentString))
{
var newValue = currentString.Substring(0, maxLengthAttr.Length);
logger?.Invoke(
$"Resolving error: {maxLengthAttr.FormatErrorMessage(propInfo.Name)}\n" +
$"Old Value: {currentString}\n" +
$"New Value: {newValue}"
);
propInfo.SetValue(item, newValue);
}
}
list.Add(item);
}
}
Example of usage (code removed for brevity):
public class Person
{
[MaxLength(4)]
public string Name { get; set; }
}
...
var personList = new List<Person>();
personList.CustomAdd(
new Person {Name = "John Doe"},
message => Debug.WriteLine(message)
);
...
As result the Jhon Doe string will be trimmed to Jhon
I have a class something like below:
Class A : B<C>
{
public A(C entity):base(entity)
{}
}
abstract class B<T>
{
public B(T entity)
{
Entity = entity;
}
public T Entity { get; private set; }
}
Class C: D
{
public string prop2{get;set;}
}
Class D
{
public string prop1{get;set;}
}
Main()
{
A obj = new A(new C());
obj.GetType().GetProperty("prop1", BindingsFlag.Instance|BindingsFlag.FlatteredHierarchy)// is null
}
I have object of class A.
I want to get property value from this object at runtime.
I am trying with
obj.GetType().GetProprty("propertyName",
BindingsFlag.FlattenHierarchy).GetValue(obj, null);
However GetProprty() is returing null as that property is declared either in D or C class.
Can someone please suggest me how to achieve this?
Thanks in advance.
GetType().GetProperty("propertyName", BindingsFlag.FlattenHierarchy)
.GetValue(obj, null);
You are misssing binding flag that specifies wheter get instance or static property:
BindingsFlag.FlattenHierarchy | BindingsFlag.Instance
According to MSDN flag BindingsFlag.Instance or BindingsFlag.Static must be specifed explicity in order to get not null values:
You must specify either BindingFlags.Instance or BindingFlags.Static
in order to get a return.
What's more, public properties are exlcuded by default. So if you property is public you need to specify additional flag:
BindingsFlag.FlattenHierarchy | BindingsFlag.Instance | BindingsFlag.Public
Remarks:
Specify BindingFlags.Public to include public properties in the
search.
If property in base is private, FlattenHierarchy will not enumerate it:
(...) private static members in inherited classes are not included
If this is your case, I am afraid that you have to manually travel through base class and search for that property.
Make also sure, that property name is valid and exists.
EDIT:
After your edit, I see the problem. Your class A is not sub-class of D class (you want to get property from D class). That is why getting property value is not working in such way.
You need to follow the following steps:
// get entity prop value
var entityValue =
(obj.GetType()
.GetProperty("Entity",
BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.Public)
.GetValue(obj));
// get prop value
var prop1Value =
entityValue.GetType()
.GetProperty("prop1",
BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.Public)
.GetValue(entityValue);
Remember to handle null values etc.
Lets assume I'm ANTLR to parse some text to produce an readonly object model that can be consumed. Numerous objects reference other objects in the object model.
The steps I'm currently taking are:
Use ANTLR 4 to parse the source into a tree (it generated)
Walk the tree to build a temporary object model (which uses strings as references)
Walk the temporary object model and create the public model
The problem with this approach is that as there is an explosion of types and mappings as the grammar grows. What approaches do compilers and other parses take to build up an object model and resolve internal references?
Source
Here's an excerpt of the source being parsed. Its simplified to illustrate the challenge.
class Class1 : Class2, Class4
{
}
class Class2 : Class3
{
}
class Class3
{
}
class Class4
{
}
Public Object Model
Here's the public object model that is the result of parsing.
public class ModelFactory
{
public IModel Create()
{
/* Some magic */
}
}
public interface IModel
{
IClass CreateClass(string name);
IEnumerable<IClass> Classes
{
get;
}
}
public interface IClass
{
void CreateGeneralization(IClass superClass);
IEnumerable<IClass> SubClasses
{
get;
}
IEnumerable<IClass> SuperClasses
{
get;
}
IModel Model
{
get;
}
string Name
{
get;
}
}
Testing
A test I wrote to validate that I got it right:
[Test]
public void ParseTest()
{
// Arrange
const string path = "MultipleInheritance.txt";
var target = new ModelParser();
// Act
var model = target.Parse(path);
// Assert
Assert.IsNotNull(model);
Assert.IsNotNull(model.Classes);
var class1 = model.Classes.FirstOrDefault(c => c.Name == "Class1");
var class2 = model.Classes.FirstOrDefault(c => c.Name == "Class2");
var class3 = model.Classes.FirstOrDefault(c => c.Name == "Class3");
var class4 = model.Classes.FirstOrDefault(c => c.Name == "Class4");
Assert.IsNotNull(class1);
Assert.IsNotNull(class2);
Assert.IsNotNull(class3);
Assert.IsNotNull(class4);
Assert.IsTrue(class1.SuperClasses.Any(c => c == class2));
Assert.IsTrue(class1.SuperClasses.Any(c => c == class4));
Assert.IsTrue(class2.SuperClasses.Any(c => c == class3));
Assert.IsEmpty(class3.SuperClasses);
Assert.IsEmpty(class4.SuperClasses);
Assert.IsTrue(class4.SubClasses.Any(c => c == class1));
}
Grammar
The grammar is simplified in order to illustrate the problem.
grammar Model;
/*
* Parser Rules
*/
model
: classDeclaration*
| EOF
;
classDeclaration
: 'class' name=Identifier (':' generalizations=typeList)?
'{'
/* attributeDeclaration* */
'}'
;
typeList
: type (',' type)*
;
type
: name=Identifier
;
/*
* Lexer Rules
*/
Identifier
: Letter (Letter|IdentifierDigit)*
;
fragment
Letter
: '\u0024'
| '\u0041'..'\u005a'
| '\u005f'
| '\u0061'..'\u007a'
| '\u00c0'..'\u00d6'
| '\u00d8'..'\u00f6'
| '\u00f8'..'\u00ff'
| '\u0100'..'\u1fff'
| '\u3040'..'\u318f'
| '\u3300'..'\u337f'
| '\u3400'..'\u3d2d'
| '\u4e00'..'\u9fff'
| '\uf900'..'\ufaff'
;
fragment
IdentifierDigit
: '\u0030'..'\u0039'
| '\u0660'..'\u0669'
| '\u06f0'..'\u06f9'
| '\u0966'..'\u096f'
| '\u09e6'..'\u09ef'
| '\u0a66'..'\u0a6f'
| '\u0ae6'..'\u0aef'
| '\u0b66'..'\u0b6f'
| '\u0be7'..'\u0bef'
| '\u0c66'..'\u0c6f'
| '\u0ce6'..'\u0cef'
| '\u0d66'..'\u0d6f'
| '\u0e50'..'\u0e59'
| '\u0ed0'..'\u0ed9'
| '\u1040'..'\u1049'
;
WS
: [ \r\t\n]+ -> skip
;
The temporary object model
Once parsed, I build up this model from the tree, which I then walk to build the public domain model.
public class TempoaryModel
{
public TempoaryModel()
{
Classes = new List<TemporaryClass>();
}
public List<TemporaryClass> Classes
{
get;
private set;
}
}
public class TemporaryClass
{
public TemporaryClass()
{
SuperClasses = new List<string>();
}
public List<string> SuperClasses
{
get;
private set;
}
public string Name
{
get;
set;
}
}
Looks like you can avoid having a temporary model if you walk the tree twice. On first walk, create instances of classes with only their name set and add them to a dictionary(class name, IClass). On second iteration, set the references between them, using the dictionary from the first step.
How to get inherited property value using reflection?
I try with BindingFlags but still trigger NullReferenceException
object val = targetObject.GetType().GetProperty("position", BindingFlags.FlattenHierarchy).GetValue(targetObject, null);
position is iherited public property and has a declared value.
EDIT:
class myParent
{
public float[] position;
public myParent()
{
this.position = new float[] { 1, 2, 3 };
}
}
class myChild : myParent
{
public myChild() : base() { }
}
myChild obj = new myChild();
PropertyInfo p = obj.GetType().GetProperty("position", BindingFlags.Instance | BindingFlags.Public);
I tried with several combinations with BindingFlags but p always is null :( ,
If you use the overload with BindingFlags you have to explicitly specify all the flags what you are interested.
Also note that: (from MSDN)
You must specify either BindingFlags.Instance or BindingFlags.Static
in order to get a return.
object val = targetObject.GetType()
.GetProperty("position",
BindingFlags.FlattenHierarchy |
BindingFlags.Instance |
BindingFlags.Public)
.GetValue(targetObject, null);
EDIT:
You have a position field not a property !.
(A good place to start learning the difference: Difference between Property and Field in C# 3.0+ especially this answer)
Change your position to a property:
public float[] position { get; set; }
Or you use the targetObject.GetType().GetField(... method to retrieve the field.
BindingFlags.FlattenHierarchy
works only for static members. Be sure to specify
BindingFlags.Instance | BindingFlags.Public
and you should get inherited properties.
I'm trying to read all of the properties of a given object, reading in only those that are declared on the object's type, excluding those that are inherited. IE:
class Parent {
public string A { get; set; }
}
class Child : Parent {
public string B { get; set; }
}
And so I want to only get B back. Reading the docs, I assumed below was what I needed, but that actually returned nothing at all.
var names = InstanceOfChild.GetType().GetProperties(BindingFlags.DeclaredOnly).Select(pi => pi.Name).ToList();
Just need a couple other BindingFlags
var names = InstanceOfChild.GetType().GetProperties(
BindingFlags.DeclaredOnly |
BindingFlags.Public |
BindingFlags.Instance).Select(pi => pi.Name).ToList();
Try this:
var names = InstanceOfChild.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly).Select(pi => pi.Name).ToList();
I added the BidningFlags.Instance and BindingFlags.Public to the search parameters which according to the MSDN documentation respectfully:
Specifies that instance members are to
be included in the search.
and
Specifies that public members are to
be included in the search.