可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a class containing several properties (all are strings if it makes any difference).
I also have a list, which contains many different instances of the class.
While creating some unit tests for my classes I decided I wanted to loop through each object in the list and then loop through each property of that object...
I thought doing this would be as simple as...
foreach (Object obj in theList)
{
foreach (Property theProperties in obj)
{
do some stufff!!;
}
}
But this didnt work! :(
I get this error...
"foreach statement cannot operate on variables of type 'Application.Object' because 'Application.Object' does not contain a public definition for 'GetEnumerator'"
Does anyone know of a way of doing this without tons of ifs and loops or without getting into anything too complex?
回答1:
Give this a try:
foreach (PropertyInfo propertyInfo in obj.GetType().GetProperties())
{
// do stuff here
}
Also please note that Type.GetProperties()
has an overload which accepts a set of binding flags so you can filter out properties on a different criteria like accessibility level, see MSDN for more details: Type.GetProperties Method (BindingFlags) Last but not least don't forget to add the "system.Reflection" assembly reference.
For instance to resolve all public properties:
foreach (var propertyInfo in obj.GetType()
.GetProperties(
BindingFlags.Public
| BindingFlags.Instance))
{
// do stuff here
}
Please let me know whether this works as expected.
回答2:
You can loop through all non-indexed properties of an object like this:
var s = new MyObject();
foreach (var p in s.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any())) {
Console.WriteLine(p.GetValue(s, null));
}
Since GetProperties()
returns indexers as well as simple properties, you need an additional filter before calling GetValue
to know that it is safe to pass null
as the second parameter.
You may need to modify the filter further in order to weed out write-only and otherwise inaccessible properties.
回答3:
Your'e almost there, you just need to get the properties from the type, rather than expect the properties to be accessible in the form of a collection or property bag:
var property in obj.GetType().GetProperties()
From there you can access like so:
property.Name
property.GetValue(obj, null)
With GetValue
the second parameter will allow you to specify index values, which will work with properties returning collections - since a string is a collection of chars, you can also specify an index to return a character if needs be.
回答4:
Sure, no problem:
foreach(object item in sequence)
{
if (item == null) continue;
foreach(PropertyInfo property in item.GetType().GetProperties())
{
// do something with the property
}
}
回答5:
Use Reflection to do this
SomeClass A = SomeClass(...)
PropertyInfo[] properties = A.GetType().GetProperties();
回答6:
A small word of caution, if "do some stuff" means updating the value of the actual property that you visit AND if there is a struct type property along the path from root object to the visited property, the change you made on the property will not be reflected on the root object.
回答7:
I couldn't get any of the above ways to work, but this worked. The username and password for DirectoryEntry are optional.
private List<string> getAnyDirectoryEntryPropertyValue(string userPrincipalName, string propertyToSearchFor)
{
List<string> returnValue = new List<string>();
try
{
int index = userPrincipalName.IndexOf("@");
string originatingServer = userPrincipalName.Remove(0, index + 1);
string path = "LDAP://" + originatingServer; //+ @"/" + distinguishedName;
DirectoryEntry objRootDSE = new DirectoryEntry(path, PSUsername, PSPassword);
var objSearcher = new System.DirectoryServices.DirectorySearcher(objRootDSE);
objSearcher.Filter = string.Format("(&(UserPrincipalName={0}))", userPrincipalName);
SearchResultCollection properties = objSearcher.FindAll();
ResultPropertyValueCollection resPropertyCollection = properties[0].Properties[propertyToSearchFor];
foreach (string resProperty in resPropertyCollection)
{
returnValue.Add(resProperty);
}
}
catch (Exception ex)
{
returnValue.Add(ex.Message);
throw;
}
return returnValue;
}
回答8:
A copy-paste solution (extension methods) mostly based on earlier responses to this question.
Also properly handles IDicitonary (ExpandoObject/dynamic) which is often needed when dealing with this reflected stuff.
Not recommended for use in tight loops and other hot paths. In those cases you're gonna need some caching/IL emit/expression tree compilation.
public static IEnumerable<(string Name, object Value)> GetProperties(this object src)
{
if (src is IDictionary<string, object> dictionary)
{
return dictionary.Select(x => (x.Key, x.Value));
}
return src.GetObjectProperties().Select(x => (x.Name, x.GetValue(src)));
}
public static IEnumerable<PropertyInfo> GetObjectProperties(this object src)
{
return src.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(p => !p.GetGetMethod().GetParameters().Any());
}