带反射的产量迭代器
我有一个数据类学生,我有一个聚合类学生。 Student 有两个字符串类型的属性:Name 和 City。
我想要做的是可以选择使用 foreach 机制来迭代哪些属性。
我编写的代码可以工作,而且可读且美观。 主要问题是性能:我使用yield 关键字的行可能效率不是很高,但问题是效率是多少?这是戏剧性的性能打击吗?
有没有更好的方法来实现这个功能? (补充:我不想允许某人修改返回的 Student 对象,因此这里提出的所有 Linq 解决方案都不好。为了更清楚地说明,我想要:
属性迭代+foreach机制集成+Student类和学生列表是只读的。 我怎样才能做到这一点?)
static void Main(string[] args)
{
Students students = new Students();
students.AddStudent(new Student { Age = 20, Name = "Stud1" , City="City1" });
students.AddStudent(new Student { Age = 46, Name = "Stud2" , City="City2"});
students.AddStudent(new Student { Age = 32, Name = "Stud3" , City="City3" });
students.AddStudent(new Student { Age = 34, Name = "Stud4" , City="city4" });
students.PropertyToIterate = eStudentProperty.City;
foreach (string studentCity in students)
{
Console.WriteLine(studentcity);
}
students.PropertyToIterate = eStudentProperty.Name;
foreach (string studentName in students)
{
Console.WriteLine(studentName);
}
}
public class Students :IEnumerable<object>
{
private List<Student> m_Students = new List<Student>();
private eStudentProperty m_PropertyToIterate = eStudentProperty.Name;
public eStudentProperty PropertyToIterate
{
get { return m_PropertyToIterate; }
set { m_PropertyToIterate = value; }
}
public void AddStudent(Student i_Student)
{
m_Students.Add(i_Student);
}
public IEnumerator<object> GetEnumerator()
{
for (int i = 0; i < m_Students.Count; ++i)
{
yield return (object)m_Students[i].GetType().GetProperty(PropertyToIterate.ToString()).GetValue(m_Students[i], null);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public enum eStudentProperty
{
Name,
Age,
City
}
public class Student
{
public string Name { get; set; }
public string City { get; set; }
public int Age { get; set; }
}
I have a data class Student, and I have an aggregate class Students.
Student has two properties of type string : Name and City.
what I want to do is have the option to choose what property to iterate using the foreach mechanism.
The code I wrote works and it's also readable and nice-looking.
The main issue is performance : the line in which I use the yield keyword is probably not very efficient , but the question is how much ? is it dramatic performance hit ?
Is there a better way to achieve this functionality?
(added: i don't want to allow someone to modify the returning Student objects, so all the Linq solutions proposed aren't good here. To make it clearer , I want:
property iteration + foreach mechanism integration + Student class and the list of students are readonly.
how can i achieve that ?)
static void Main(string[] args)
{
Students students = new Students();
students.AddStudent(new Student { Age = 20, Name = "Stud1" , City="City1" });
students.AddStudent(new Student { Age = 46, Name = "Stud2" , City="City2"});
students.AddStudent(new Student { Age = 32, Name = "Stud3" , City="City3" });
students.AddStudent(new Student { Age = 34, Name = "Stud4" , City="city4" });
students.PropertyToIterate = eStudentProperty.City;
foreach (string studentCity in students)
{
Console.WriteLine(studentcity);
}
students.PropertyToIterate = eStudentProperty.Name;
foreach (string studentName in students)
{
Console.WriteLine(studentName);
}
}
public class Students :IEnumerable<object>
{
private List<Student> m_Students = new List<Student>();
private eStudentProperty m_PropertyToIterate = eStudentProperty.Name;
public eStudentProperty PropertyToIterate
{
get { return m_PropertyToIterate; }
set { m_PropertyToIterate = value; }
}
public void AddStudent(Student i_Student)
{
m_Students.Add(i_Student);
}
public IEnumerator<object> GetEnumerator()
{
for (int i = 0; i < m_Students.Count; ++i)
{
yield return (object)m_Students[i].GetType().GetProperty(PropertyToIterate.ToString()).GetValue(m_Students[i], null);
}
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
public enum eStudentProperty
{
Name,
Age,
City
}
public class Student
{
public string Name { get; set; }
public string City { get; set; }
public int Age { get; set; }
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
如果学生可以是一个结构,那么它将处理学生项目的只读部分。否则,只需在构造函数中设置 Student 类的属性并删除公共设置即可。
编辑:好的,没有结构。将学生更改为班级
if student can be a struct then that will handle the readonly part of the student item. Otherwise, just make the properties of the Student class set in the constructor and remove the public set.
edit: okay, no struct. changing Student to a class
添加一个接口怎么样?
然后您可以使用 LINQ 解决方案,并且 Student 对象无法更改。
What about adding an interface?
Then you could use the LINQ solution and the Student objects can not be altered.
由于每次迭代都重复反映类型,您可能会遇到性能损失。为了避免不必要地调用
GetType()
,请将其从循环中拉出。另外,由于类型是已知的(例如,Student),因此您可以使用typeof
获得一些编译时间效率。另一方面,您可以通过返回通用版本来满足非通用
GetEnumerator()
要求。You may be experiencing performance loss due to repeatedly reflecting the type every iteration. To avoid unnecessary calls to
GetType()
, pull it out of the loop. Also, since the type is known (e.g., Student), you can gain some compile time efficiency usingtypeof
.On an aside, you can satisfy the non-generic
GetEnumerator()
by returning the generic version.我个人非常喜欢LukeH的回答。唯一的问题是必须使用静态只读委托包装对象而不是原始的 eStudentProperty 枚举来定义静态 StudentProperty 类。根据您的情况,调用者可能无法轻松使用此功能。
该代码的优点是每个
StudentProperty
对象根据其关联属性进行强类型化,从而允许 EnumerateBy 方法返回强类型化IEnumerable
。以下是我的想法。它与 LukeH 的答案非常相似,因为我提供了一个类似于 LukeH 的
EnumerateBy
方法的PropertyValues
方法,尽管我的方法返回一个IEnumerable
(非-通用的)。我的实现的一个问题是,如果您正在迭代值类型属性(例如 Age),那么枚举器中将会发生一些装箱。但是,如果消费者对正在迭代的属性不够了解,无法调用我提供的
Ages
方法而不是PropertyValues(eStudentProperty.Age)
,则装箱无论我是否能够返回IEnumerable
还是IEnumerable
,它很可能会出现在调用者的代码中。因此,我认为任何需要调用PropertyValues
方法的人都不能调用Names
、Cities
或Ages 方法将无法避免任何实现的装箱。
I personally like LukeH's answer quite a bit. The only issue is having to define the static StudentProperty class with static readonly delegate wrapper objects rather than your original eStudentProperty enum. Depending on your situation, this might not be easily usable by the caller.
The advantage of that code is that each
StudentProperty<T>
object is strongly typed according to its associated property, allowing the EnumerateBy method to return a strongly typedIEnumerable<T>
.Below is what I came up with. It's very similar to LukeH's answer in that I've provided a
PropertyValues
method similar to LukeH'sEnumerateBy
method, although my method returns anIEnumerable
(non-generic).An issue with my implementation is that if you are iterating over a value-type property (such as Age), then there will be some boxing going on within the enumerator. However, if the consumer doesn't know enough about the property being iterated to call the
Ages
method that I've provided rather thanPropertyValues(eStudentProperty.Age)
, then boxing will most likely occur in the caller's code regardless of whether I'm able to return anIEnumerable<int>
or anIEnumerable
. So I would argue that anyone who needs to call thePropertyValues
method because they cannot call theNames
,Cities
, orAges
methods will not be able to avoid boxing with any implementation.您可以使用 lambda 执行类似的操作
PropertyToIterate 然后采用
Func
您可以这样设置:GetEnumerator 可以使用 linq 实现,如下所示:
或者不使用 linq,如下所示
You could use lambda's to do something similar
PropertyToIterate would then take a
Func<Student, object>
that you can set this way:GetEnumerator can be implemented with linq like this:
Or without linq like this
为了回应您的编辑,像这样的事情怎么样......
In response to your edit, how about something like this...
为什么不简单地使用 Linq 获取属性并保留学生的原始枚举,以便您可以迭代 Students 类中的所有学生。
Why not simply get the properties with Linq and keep the original Enumeration of the students so you can iterate all the students in the Students class.