如何创建自定义*只写*依赖属性?

发布于 2024-08-03 01:39:04 字数 1084 浏览 4 评论 0原文

我需要知道创建只写依赖属性的过程是什么。我可以看到 DependencyProperty 类没有用于只写属性的特殊“Register”方法,但我不知道 RegisterAttached 方法是否适用于我想要执行的操作。

此属性需要是依赖属性,而不是简单的 CLR 属性。在内部,我的类需要对此属性进行 PropertyChangedCallback 以保持稳定。

我知道可以创建只写依赖属性,因为它在以下位置中明确说明:
Pro C# 2008 和 .NET 3.5 平台,第 1061 页
然而,这是我可以在同一页面上找到“依赖属性”和“只写”的唯一地方。这位作者显然认为没有必要向读者展示除基本读写依赖属性之外的任何内容的过程。当然,这本书可能是一堆废话——但这本书看起来相当标准,所以我认为作者是正确的。我认为互联网上信息的缺乏源于这样一个事实:通常没有人需要建造这样的房产。

我知道想要创建自己的只写依赖属性听起来很可疑。我向你保证这在我想要的地方是有意义的。我的类有一个属性,其值仅对设置它的对象有用。如果另一个对象稍后请求该属性的值,则在不知道设置器的原始上下文的情况下,它将无法从该值中获得任何理性意义。

该属性不用于提供信息。让外部对象尝试以这种方式使用属性值是有问题的、危险的和安全风险。所以我认为最好的设计是禁止对该属性的读取操作。任何使用我的类的人都会发现他们被迫按照预期的方式使用该类,这最终会变得更好、更干净。

I need to know what the procedure is for making a write-only dependency-property. I can see that the DependencyProperty class does not have a special "Register" method for write-only properties, but I have no idea whether the RegisterAttached method may apply to what I am trying to do.

This property needs to be a dependency-property, rather than a simple CLR property. Internally, my class requires a PropertyChangedCallback on this property in order to remain stable.

I know that write-only dependency-properties can be created, because it is stated quite clearly in:
Pro C# 2008 and the .NET 3.5 Platform, Page 1061.
However, this is the only place where I can even find "dependency property" and "write only" on the same page. And this author apparently did not think it was necessary to actually show the reader the procedure for anything other than a basic read-write dependency-property. Of course, this book could be a load of BS - but this book looks pretty standard, so I think it's a pretty safe bet that the author is correct. I assume the lack of information on the internet stems from the fact that nobody generally needs to make a property like this.

I know it sounds very questionable to want to make your own write-only dependency-property. I assure you it makes sense where I want it. My class has a property whose value is only useful to the object setting it. If another object were to request the value of this property later, it wouldn't be able to make any rational sense out of the value without knowing the original context of the setter.

This property is not intended to be used for informational purposes. Letting outside objects attempt to use the property value this way is problematic, dangerous, and a security risk. So I believe the best design is to prohibit read operations on this property. Anyone using my class will find that they are forced to use the class the way it was intended, which will work out much better and cleaner in the end.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

百善笑为先 2024-08-10 01:39:04

你不能,这似乎是设计使然。虽然我可以理解您对上述书籍的处理方式,并且绝不质疑其质量,但我仍然认为这是某种复制粘贴或类似的问题。这是我的推理:

WPF 属性系统代码

WPF 属性系统设计

  • 更重要的是,'其 XAML 处理器的当前 WPF 实现本质上是依赖属性感知的。加载二进制 XAML 并处理依赖属性的属性时,WPF XAML 处理器使用依赖属性的属性系统方法。这有效地绕过了属性包装器。',请参阅 XAML 加载和依赖项属性
  • 最重要的是,依赖属性通常应被视为公共属性。 Windows Presentation Foundation (WPF) 属性系统的本质阻止了对依赖项属性值进行安全保证的能力。,请参阅 依赖属性安全

特别是后两点概述了设计约束,即依赖属性值始终可以通过 GetValue()/SetValue() ,无论它们的 CLR 包装器是访问受限还是完全可用,唯一的例外是专门考虑的 只读依赖属性

因此,正如 Jeffs答案已经暗示,仅删除 getter 例如并不能真正阻止任何人通过 GetValue(),尽管这至少可能“减少自定义类立即暴露的命名空间”。任何此类语义解决方法的用处是使属性值的可见性/可访问性稍微降低,并且检索到的值对客户端来说本质上是无用的,如 Jeff 当然取决于您的特定场景。

You can't, this appears to be by design. While I can understand your approach to the mentioned book and am in no way questioning its quality, I'd still presume this to be some sort of copy&paste or similar issue. Here is my reasoning:

WPF property system code

WPF property system design

  • More important, 'The current WPF implementation of its XAML processor is inherently dependency property aware. The WPF XAML processor uses property system methods for dependency properties when loading binary XAML and processing attributes that are dependency properties. This effectively bypasses the property wrappers.', see XAML Loading and Dependency Properties.
  • Most important, 'Dependency properties should generally be considered to be public properties. The nature of the Windows Presentation Foundation (WPF) property system prevents the ability to make security guarantees about a dependency property value.', see Dependency Property Security.

Especially the latter two points are outlining the design constraint, that dependency property values are always accessible via GetValue()/SetValue(), no matter whether their CLR wrappers are access restricted or available at all, with the only exception being the specifically accounted for Read-Only Dependency Properties.

Consequently, as Jeffs answer implies already, just removing the getter for example does not really prevent anyone accessing the property via GetValue(), though this may at least 'reduce the immediately exposed namespace of a custom class'. The usefulness of any such semantic workaround of making the property value somewhat less visible/accessible and the retrieved value inherently useless for clients as suggested by Jeff depends on your particular scenario of course.

几度春秋 2024-08-10 01:39:04

有趣的是,这绝对是一种罕见的情况,我有兴趣了解更多它的功能。

您是否会考虑通过绑定或 GetValue 为读取提供无效值(例如 null),而只是没有 CLR getter?

使用私有 DependencyProperty 来存储您关心的“真实”值,仅存储私有成员变量。

在属性更改回调中,始终将值恢复为原始值,同时存储设置的新值。

我现在大部分时间都花在 Silverlight 控件开发上,因此该属性适用于 WPF 和 Silverlight 领域,并且不使用强制转换或类似的任何有趣的东西。不过,也许它会让你走上正确的道路。

    /// <summary>
    /// Sets the write-only dependency property.
    /// </summary>
    public string MyWriteOnlyDependencyProperty
    {
        set { SetValue(MyWriteOnlyDependencyPropertyProperty, value); }
    }

    private string _theRealSetValue;

    private bool _ignorePropertyChange;

    /// <summary>
    /// Identifies the MyWriteOnlyDependencyProperty dependency property.
    /// </summary>
    public static readonly DependencyProperty MyWriteOnlyDependencyPropertyProperty =
        DependencyProperty.Register(
            "MyWriteOnlyDependencyProperty",
            typeof(string),
            typeof(TemplatedControl1),
            new PropertyMetadata(null, OnMyWriteOnlyDependencyPropertyPropertyChanged));

    /// <summary>
    /// MyWriteOnlyDependencyPropertyProperty property changed handler.
    /// </summary>
    /// <param name="d">TemplatedControl1 that changed its MyWriteOnlyDependencyProperty.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnMyWriteOnlyDependencyPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TemplatedControl1 source = d as TemplatedControl1;
        if (source._ignorePropertyChange)
        {
            source._ignorePropertyChange = false;
            return;
        }
        string value = e.NewValue as string;

        source._theRealSetValue = value;

        // Revert, since this should never be accessible through a read
        source._ignorePropertyChange = true;
        source.SetValue(e.Property, e.OldValue);
    }

Interesting, this is definitely a rare scenario, I'd be interested to hear more in what it enables.

Would you consider the idea of providing an invalid value (such as null) for reads through binding or GetValue, while just not having a CLR getter?

Either use a private DependencyProperty to store the "real" value you care about, or just a private member variable.

In the property changed callback, always revert the value back to the original value, while storing away the new value that was set.

I spend most of my time doing Silverlight control development now, so this property works in WPF and Silverlight-land, and doesn't use coercian or anything fun like that. Maybe it gets you going on the right track, though.

    /// <summary>
    /// Sets the write-only dependency property.
    /// </summary>
    public string MyWriteOnlyDependencyProperty
    {
        set { SetValue(MyWriteOnlyDependencyPropertyProperty, value); }
    }

    private string _theRealSetValue;

    private bool _ignorePropertyChange;

    /// <summary>
    /// Identifies the MyWriteOnlyDependencyProperty dependency property.
    /// </summary>
    public static readonly DependencyProperty MyWriteOnlyDependencyPropertyProperty =
        DependencyProperty.Register(
            "MyWriteOnlyDependencyProperty",
            typeof(string),
            typeof(TemplatedControl1),
            new PropertyMetadata(null, OnMyWriteOnlyDependencyPropertyPropertyChanged));

    /// <summary>
    /// MyWriteOnlyDependencyPropertyProperty property changed handler.
    /// </summary>
    /// <param name="d">TemplatedControl1 that changed its MyWriteOnlyDependencyProperty.</param>
    /// <param name="e">Event arguments.</param>
    private static void OnMyWriteOnlyDependencyPropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TemplatedControl1 source = d as TemplatedControl1;
        if (source._ignorePropertyChange)
        {
            source._ignorePropertyChange = false;
            return;
        }
        string value = e.NewValue as string;

        source._theRealSetValue = value;

        // Revert, since this should never be accessible through a read
        source._ignorePropertyChange = true;
        source.SetValue(e.Property, e.OldValue);
    }
夏末 2024-08-10 01:39:04

看起来您可以通过依赖属性定义中应用的 FrameworkPropertyMetadata 使用与该属性关联的 CoerceValueCallback 。只需安装一个回调,该回调接受第二个参数(新值),通过您自己的只写机制将其传递给对象,然后返回 null (或者对于值类型,default(T) )。

确实,“.NET 会记住强制转换之前的原始值”,但它不会通过数据绑定进行传播。调用 GetValue 将返回强制值,该值不会泄漏任何内容。

我使用它来为我的主要属性(一个字节序列)的值实现单向便利设置器。例如,用户可以绑定字符串,以将主要属性设置为编码字节(ASCII 或 UTF-8,具体取决于设置的属性)。但并非所有字节序列都是有效的 UTF-8,因此不可能反转转换并通过便利属性读回字符串。

public string AsciiData
{
    set { BinaryArray = Encoding.ASCII.GetBytes(value); }
}

public static readonly DependencyProperty AsciiDataProperty =
    DependencyProperty.Register("AsciiData",
        typeof(string),
        typeof(HexView),
        new FrameworkPropertyMetadata(null, CoerceAsciiData));

private static object CoerceAsciiData(DependencyObject target, object value)
{
    (target as HexView).AsciiData = value as string;
    return null;
}

强制处理程序可以通过元数据替换来删除,因此这不能提供安全性,但可以防止开发人员意外地以错误的方式创建耦合。

It looks like you can use the CoerceValueCallback associated with the property via the FrameworkPropertyMetadata applied in the dependency property definition. Just install a callback that takes the second argument, the new value, passes it to the object via your own write-only mechanism, then returns null (or for value types, default(T)).

It's true that ".NET remembers the original value prior to coercion", but it won't be propagated via data-binding. Calls to GetValue will return the coerced value, which doesn't leak anything.

I'm using this to implement one-way convenience setters for the value of my primary property, which is a sequence of bytes. A user can bind a string, for example, to set the primary property to the encoded bytes (ASCII or UTF-8, depending what property is set). But not all byte sequences are valid UTF-8, so it isn't possible to reverse the conversion and read a string back through the convenience property.

public string AsciiData
{
    set { BinaryArray = Encoding.ASCII.GetBytes(value); }
}

public static readonly DependencyProperty AsciiDataProperty =
    DependencyProperty.Register("AsciiData",
        typeof(string),
        typeof(HexView),
        new FrameworkPropertyMetadata(null, CoerceAsciiData));

private static object CoerceAsciiData(DependencyObject target, object value)
{
    (target as HexView).AsciiData = value as string;
    return null;
}

The coercion handler can be removed via metadata replacement, so this isn't providing security, but it will prevent developers from accidentally creating coupling in wrong ways.

等风也等你 2024-08-10 01:39:04

我很困惑为什么你不能让“get”返回任何有用的东西?

但此外,也许您只是没有实现杰夫示例中的“OnMyWriteOnlyDependencyPropertyPropertyChanged”。

如果没有人能读到它,就没有真正的理由举办这个活动,对吗?

I'm confused as to why you can't just have the 'get' return nothing useful?

But furthermore, perhaps you just don't implement the 'OnMyWriteOnlyDependencyPropertyPropertyChanged', in Jeff's example.

No real reason to have the event, if no-one can read it, right?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文