C# WinForm中如何通过循环动态访问类的HitN属性?
嘿,这个场景我太熟悉了——硬编码处理可变数量的HitBox简直是后期维护的噩梦!给你两个实用的解决思路,优先推荐第一种,完全符合C#的设计习惯:
方案一:重构类,用集合存储HitBox点(强烈推荐)
这是最优雅、最易维护的方式。把原来零散的Hit1、Hit2、Hit3属性改成一个集合,比如List<Point>或者Point[],这样不管HitBox数量怎么变,循环遍历就行。
首先修改你的类定义(替换成你实际的类名):
public class YourCustomClass { public string Name { get; set; } // 其实可以直接用HitBoxes.Count替代NumberOfHitBoxes,或者保留做校验用 public int NumberOfHitBoxes { get; set; } // 用集合存储所有HitBox的起点 public List<Point> HitBoxes { get; set; } = new List<Point>(); // 对应你的Input/Output点,也建议改成集合,比如: public List<Point> InputPoints { get; set; } = new List<Point>(); public List<Point> OutputPoints { get; set; } = new List<Point>(); public string Type { get; set; } }
然后修改MouseMove事件的逻辑,直接循环集合:
picture.MouseMove += (sender, e) => { var c = sender as PictureBox; if (null == c) return; bool h = false; if (_dragging) { c.Top = e.Y + c.Top - _yPos; c.Left = e.X + c.Left - _xPos; } else { // 遍历所有HitBox for (int i = 0; i < p.HitBoxes.Count; i++) { Point hitPoint = p.HitBoxes[i]; // 判断鼠标是否在当前HitBox范围内 if (e.X > hitPoint.X && e.X < hitPoint.X + hitw && e.Y > hitPoint.Y && e.Y < hitPoint.Y + hitw) { hitbox(c, hitPoint.X, hitPoint.Y, 8); h = true; tb.Text = p.Type; // 根据索引取对应的Input/Output点,这里根据你的业务调整 // 比如前两个是Input,第三个是Output,那可以加个判断: hitok = i < 2 ? p.InputPoints[i] : p.OutputPoints[i - 2]; // 找到匹配的就跳出循环,避免重复处理 break; } } } if (!h) { picture.Refresh(); h = false; hitok = new Point(0,0); } };
为什么强烈推荐这个?因为集合就是为可变数量的元素设计的,代码可读性拉满,性能也比反射好,后期要加HitBox只需要往集合里加元素,完全不用改判断逻辑。
方案二:用反射动态访问属性(仅适合无法修改类的场景)
如果因为某些限制不能修改原来的类结构,那可以用反射来动态获取Hit1、Hit2这类命名规律的属性。不过这个方式缺点不少,谨慎使用。
修改后的MouseMove代码:
picture.MouseMove += (sender, e) => { var c = sender as PictureBox; if (null == c) return; bool h = false; if (_dragging) { c.Top = e.Y + c.Top - _yPos; c.Left = e.X + c.Left - _xPos; } else { // 从1循环到NumberOfHitBoxes for (int i = 1; i <= p.NumberOfHitBoxes; i++) { // 动态获取HitN属性 var hitProp = p.GetType().GetProperty($"Hit{i}"); if (hitProp == null) continue; // 防止属性不存在报错 Point hitPoint = (Point)hitProp.GetValue(p); if (e.X > hitPoint.X && e.X < hitPoint.X + hitw && e.Y > hitPoint.Y && e.Y < hitPoint.Y + hitw) { hitbox(c, hitPoint.X, hitPoint.Y, 8); h = true; tb.Text = p.Type; // 动态获取对应的Input/Output点,这里要匹配你的命名规则 var targetProp = i switch { 1 => p.GetType().GetProperty("Input1Point"), 2 => p.GetType().GetProperty("Input2Point"), 3 => p.GetType().GetProperty("Output1Point"), _ => null }; if (targetProp != null) { hitok = (Point)targetProp.GetValue(p); } break; } } } if (!h) { picture.Refresh(); h = false; hitok = new Point(0,0); } };
反射的问题:性能不如直接访问属性,代码可读性差,而且如果属性名改了,编译时不会报错,运行时才会出问题。所以除非万不得已,别用这个方法。
额外小建议
可以把“判断鼠标是否在HitBox内”的逻辑封装成一个单独的方法,代码更简洁:
private bool IsMouseInHitBox(Point mousePos, Point hitBoxOrigin, int hitBoxSize) { return mousePos.X > hitBoxOrigin.X && mousePos.X < hitBoxOrigin.X + hitBoxSize && mousePos.Y > hitBoxOrigin.Y && mousePos.Y < hitBoxOrigin.Y + hitBoxSize; }
这样循环里只需要调用IsMouseInHitBox(e.Location, hitPoint, hitw)就行,后期要调整判断逻辑也只需要改这一个方法。
内容的提问来源于stack exchange,提问作者Futec
相关产品推荐
相关产品推荐

