【问题标题】:How can I initialize only properties that have a value?如何仅初始化具有值的属性?
【发布时间】:2017-01-09 09:47:45
【问题描述】:

我有一个继承自 System.Windows.Forms.Control 的类 CustomControl。 我还将创建一个名为 GraphicsData 的新类,它将包含有关我的 CustomControl 的所有图形信息(我需要这个,因为它更容易序列化数据以将其保存在 DB、json 等中)

CustomControl 对象将在初始化时(在构造函数中)获取 GraphicsData,我希望它获取在 GraphicsData 中具有值的所有属性(有时我不想从 GraphicsData 初始化所有属性,我希望它们保持 System.Windows.Forms.Control 类的默认值)。

问题是大多数属性不可为空,我无法检查它们是否为空,所以我不能做一个简单的:

customControl.BackColor = graphicsData.BackColor.HasValue ? graphicsData.BackColor.Value : BackColor;

如果我创建自己的 Nullable 类,我当然可以解决这个问题,但这变得非常丑陋且难以理解代码。此外,在需要时添加新属性非常困难。

现在,我所做的并且我认为这是一种更清洁的方法如下:

GraphicsData 类:

public class GraphicsData : INotifyPropertyChanged
{
    private readonly List<string> _initializedProperties = new List<string>();
    public List<string> InitializedProperties { get { return _initializedProperties; } }

    public event PropertyChangedEventHandler PropertyChanged;

    private Size _size;
    private Point _location;
    private AnchorStyles _anchor;
    private Color _backColor;
    private Image _backgroundImage;
    private Cursor _cursor;
    private Font _font;
    private Color _foreColor;
    private bool _enabled;
    private bool _visible;

    public Size Size
    {
        get { return _size; }
        set
        {
            _size = value;
            OnPropertyChanged("Size");
        }
    }

    public Point Location
    {
        get { return _location; }
        set
        {
            _location = value;
            OnPropertyChanged("Location");
        }
    }

    public AnchorStyles Anchor
    {
        get { return _anchor; }
        set
        {
            _anchor = value;
            OnPropertyChanged("Anchor");
        }
    }

    public Color BackColor
    {
        get { return _backColor; }
        set
        {
            _backColor = value;
            OnPropertyChanged("BackColor");
        }
    }

    public Image BackgroundImage
    {
        get { return _backgroundImage; }
        set
        {
            _backgroundImage = value;
            OnPropertyChanged("BackgroundImage");
        }
    }

    public Cursor Cursor
    {
        get { return _cursor; }
        set
        {
            _cursor = value;
            OnPropertyChanged("Cursor");
        }
    }

    public Font Font
    {
        get { return _font; }
        set
        {
            _font = value;
            OnPropertyChanged("Font");
        }
    }

    public Color ForeColor
    {
        get { return _foreColor; }
        set
        {
            _foreColor = value;
            OnPropertyChanged("ForeColor");
        }
    }

    public bool Enabled
    {
        get { return _enabled; }
        set
        {
            _enabled = value;
            OnPropertyChanged("Enabled");
        }
    }

    public bool Visible
    {
        get { return _visible; }
        set
        {
            _visible = value;
            OnPropertyChanged("Visible");
        }
    }

    protected void OnPropertyChanged(string propertyName)
    {
        if (!_initializedProperties.Contains(propertyName))
            _initializedProperties.Add(propertyName);

        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

在我的自定义控件中,我有一个方法:

public void LoadGraphics()
    {
        var initializedProperties = graphics.InitializedProperties;
        foreach (string propertyName in initializedProperties)
        {
            var value = graphics.GetType()
                                .GetProperty(propertyName)
                                .GetValue(graphics, null);
            _customControl.GetType()
                          .GetProperty(propertyName)
                          .SetValue(_customControl, value, null);
        }
    }

基本上,我创建了一个名为 InitializedPropertiesList,并在属性“set”中添加了列表中的属性。 之后,在我的 CustomControl 中使用 reflection,我可以加载所有初始化的属性。

我实现了 INotifyPropertyChanged,因为我还想在每次更改 GraphicsData 中的属性时更改 customControl 属性。

这是做我想做的事情的正确方法吗?我不认为反射代码可读性好,我担心性能。

【问题讨论】:

  • 你可以做其他方式,将你的自定义控件传递给GraphicsData,内部属性执行更新而不反射:_backColor = value; owner.BackColor = value;
  • 哦,是的,现在我意识到我也可以做到这一点。我不知道我今天在想什么!

标签: c# reflection properties


【解决方案1】:

使用可为空的值是实现此目的的一种更简单的方法。

C# 已经有一个内置的 Nullable 类,但它还提供了一种简单的方法来使值可以为空,而不会像 Nullable 类引入的过多冗长:?

通过将? 运算符附加到值类型,您的所有值都可以为空:

private Size? _size;
private Point? _location;
private AnchorStyles? _anchor;
private Color? _backColor;
private Image _backgroundImage;
private Cursor _cursor;
private Font _font;
private Color? _foreColor;
private bool? _enabled;
private bool? _visible;

您的LoadGraphics 方法可以轻松检查GraphicsData 属性是否具有非空值,如果是,则设置相应的控件属性。

public void LoadGraphics(GraphicsData gfx)
{
    // It may be permissible to utilize a null value for BackgroundImage!
    // In this case, utilizing a separate field (IsBackgroundImageSet) may be a necessary
    if (gfx.BackgroundImage != null) { _customControl.BackgroundImage = gfx.BackgroundImage; }

    if (gfx.Size != null) { _customControl.Size = gfx.Size.Value; }
    if (gfx.Location != null) { _customControl.Location = gfx.Location.Value }
    if (gfx.Anchor != null) { _customControl.Anchor = gfx.Anchor.Value; }
    if (gfx.BackColor != null) { _customControl.BackColor = gfx.BackColor .Value; }
    if (gfx.Cursor != null) { _customControl.Cursor = gfx.Cursor; }
    if (gfx.Font != null) { _customControl.Font = gfx.Font; }
    if (gfx.Color != null) { _customControl.Color = gfx.Color.Value; }
    if (gfx.Enabled != null) { _customControl.Enabled = gfx.Enabled.Value; }
    if (gfx.Visible != null) { _customControl.Visible = gfx.Visible.Value; }
}

【讨论】:

  • 这是我要避免的。
  • 基本上,如果我会做 customControl.BackgroundImage = graphis.BackgroundImage 没有'?'运算符,我将覆盖 Control 类中的默认 backgroundImage 甚至出错
  • 我不相信你会这样做。如果您正在考虑使用反射来检查设置的属性,那么您已经偏离了简单的路径。到目前为止,使用可为空的值是更简单的方法。
  • 好的,我知道'?'操作员,我可以肯定地使用它,但我担心图像、光标和字体。当我执行 _customControl.Cursor = graphics.Cursor; 时,我不会收到错误“对象引用未设置为对象实例”吗?如果我没有在 Graphics 中初始化 Cursor 属性?
  • 看看更新后的代码。您需要确定的一件事是属性是否允许空值,或者它是否表示缺失值。在 Size 的情况下,空值表示缺失值,并且 GraphicsData 值不应该被复制。在 BackgroundImage 的情况下,空值可能是允许的值。在 Cursor 的情况下,它可能不是。因此,可能需要一些错误处理代码(可能像默认为 Cursors.Default 一样简单)。
猜你喜欢
  • 1970-01-01
  • 2018-05-15
  • 1970-01-01
  • 2018-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-05
  • 1970-01-01
相关资源
最近更新 更多