如何初始化仅具有值的属性?

3
我有一个名为CustomControl的类,它继承自System.Windows.Forms.Control。 我还将创建一个名为GraphicsData的新类,其中包含关于CustomControl的所有图形信息(我需要这个因为更容易将数据序列化以保存到数据库、json等)。
在初始化CustomControl对象时(即在构造函数中),它将获取GraphicsData信息,并获得GraphicsData中所有具有值的属性(有时我不想从GraphicsData中初始化所有属性,而是希望它们保留System.Windows.Forms.Control类的默认值)。
问题在于,大多数属性都不可为空,因此我无法检查它们是否为null,所以我不能采用简单的方法:
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);
        }
    }

我创建了一个名为InitializedProperties的List,并在属性“set”中将属性添加到该列表中。 然后,在我的CustomControl中使用reflection,我可以加载所有已初始化的属性。

我实现了INotifyPropertyChanged,因为我希望在GraphicsData中更改属性时每次都更改customControl属性。

这是我想要做的正确方式吗?我认为反射代码不太易读,并且我担心性能问题。


1
你可以采用另一种方式,将自定义控件传递给“GraphicsData”,并在属性内部执行更新而无需反射:“_backColor = value; owner.BackColor = value;”。 - Alessandro D'Andria
哦,是啊,现在我意识到我也可以这样做。我不知道今天我在想什么! - Dev Catalin
1个回答

3
使用可空值是实现此目的的一种更简单的方法。
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; }
}

这就是我想要避免的。 - Dev Catalin
基本上,如果我执行customControl.BackgroundImage = graphis.BackgroundImage,而这个语句没有'?'运算符,那么我将会覆盖掉Control类中默认的背景图片,甚至可能会出现错误。 - Dev Catalin
1
我不相信你这么做。如果你想使用反射来检查设置属性,那么你已经偏离了简单的道路。使用可空值绝对是更容易的方法。 - Brett Wolfington
好的,我理解了'?'运算符并且可以使用它,但是我担心Image、Cursor和Font。如果我没有在Graphics中初始化Cursor属性,那么当我执行_customControl.Cursor = graphics.Cursor;时,我不会得到"对象引用未设置为对象实例"的错误吗? - Dev Catalin
看一下更新后的代码。你需要确定的一件事是,属性是否允许为 null 值,还是代表缺失值。在 Size 的情况下,null 值代表缺失值,而 GraphicsData 值应该不被复制。在 BackgroundImage 的情况下,null 值可能是允许的值。在 Cursor 的情况下,它可能不是这样。因此,一些错误处理代码(可以简单地默认为 Cursors.Default)可能是必要的。 - Brett Wolfington
好的,我明白了。一开始我尝试了你提供的解决方案,但是出现了一些错误,然后我把事情搞得过于复杂了。但现在我明白了重点以及它的工作原理。谢谢! - Dev Catalin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接