6

我有一个有点简单的包装类有一点问题。

它看起来像这样:

public class Wrapper<T>
{
  private T _value;

  public Wrapper<T>(T value)
  {
    _value = value;
  }

  public static implicit operator Wrapper<T>(T value)
  {
    return new Wrapper<T>(value);
  }

  public static implicit operator T(Wrapper<T> value)
  {
    return value._value;
  }
}

我已经覆盖了从和到 T 的隐式转换器,所以它的行为几乎就像 T 本身的一个实例。

例如

Wrapper<int> foo = 42;

但是,在将 Wrapper 的一个实例分配给另一个时,我遇到了一个小问题,因为我只想分配第二个 Wrapper 类的值。

所以现在,我必须这样做:

Wrapper<int> foo = 42;
Wrapper<int> bar = (int)foo;

或者通过属性公开 _value。

但是,由于这是在库中,并且我不希望用户依赖于记住这一点,你们知道我如何模仿覆盖赋值运算符吗?

仅更改指针的问题(就像将类实例分配给另一个时所做的那样)是我有一个指向这些 Wrapper 对象的指针字典,因此我不能一直更改它们,因为字典会停止然后匹配。

我可以看看这是否有点令人困惑,所以如果我遗漏了任何重要的内容,请随时询问:-)

4

8 回答 8

5

由于赋值运算符不能重载,因此没有真正好的解决方案。正如其他人指出的那样,使用结构将为您提供所需的赋值语义,但随后您将面临值语义——通常不是一件好事。

一种选择是重载构造函数:

public Wrapper(Wrapper<T> w)
{
    _value = w._value;
}

这将导致以下语法:

Wrapper<int> foo = 42;
Wrapper<int> bar = new Wrapper<int>(foo);

虽然比你所拥有的更冗长,但它读起来更好。

或者您可以添加一个Clone方法(而不是ICloneable接口),以便您可以编写:

Wrapper<int> bar = foo.Clone();

你可以变得非常有创意并重载一些运算符,使其基本上什么都不做。不过,我不建议这样做。对这类事情使用运算符重载通常会使代码变得晦涩难懂并且经常中断。

于 2009-03-14T05:45:23.243 回答
3

您可以将 Wrapper<T> 设为 a struct。但是我不确定这是否适合您的应用程序设计。

于 2009-03-14T02:46:28.103 回答
1

不要以两种方式隐式地投射你的包装器。

public class DBValue<T>
{
    public static implicit operator DBValue <T>(T value)
    {
         return new DBValue<T>(value);
    }

    public static explicit operator T(DBValue <T> dbValue)
    {
         return dbValue.Value;
    }

    private readonly T _value;
    public T Value { get { this._value; } }

    public DBValue(T value)
    {
         this._value = value;
    }
}

转换 from DBValue<T>toT是一种有损转换(至少,您失去了它是来自数据库的值的事实),并且最佳实践应该是明确的。DBValue<T>如果你通过转换 from to没有丢失任何东西T,你还不如只使用返回的属性T

基本上,您已经明白了为什么不应该尝试这样做:如果可以用 DBValue 代替 T,反之亦然,编译器(或开发人员)如何知道选择哪一个?

要求下游开发者编写:

string value = MyProperty.Value

或者

string value = (string)MyProperty

代替

string value = MyProperty

...不是那么繁琐,并确保每个人都知道到底发生了什么。

编辑:

要真正回答这个问题,您不能覆盖参考分配 - 或让它看起来像你有 - 但你不应该真的需要。

于 2009-03-14T13:19:13.583 回答
1

如果您查看 Nullable<T>... 它与您在此处所做的事情非常相似,它会使用 .Value 属性公开内部值。

仅更改指针的问题(就像将类实例分配给另一个时所做的那样)是我有一个指向这些 Wrapper 对象的指针字典,因此我不能一直更改它们,因为字典会停止然后匹配。

我不确定我是否遵循这一点,您在字典中究竟存储了什么?因为如果您要存储引用,CLR 将根据需要更新它们。

于 2009-03-13T22:37:23.600 回答
0

这个旧帖子剧照需要额外的信息才能完成。很明显,由于 = 运算符无法重载,因此无法实现原始所需的行为,同样,C# 也不能被“欺骗”将对象转换为自己的类型……它总是归结为类引用分配。但 Steffen 的进一步帖子显示 Wrapper 类不仅用于局部变量,还用作类字段类型可以使用所需的语义,并通过使用类属性而不是公共字段来维护内部字典的完整性。


即使保留原始给定Wrapper<T>类及其隐式运算符,这里的代码也可以工作:

public class CustomerTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

public class UserTable
{
    private Wrapper<string> _Name;
    public Wrapper<string> Name {
        get { return _Name; }
        set { _Name = (string)value; }
    }
}

如果进行此更改,它不会破坏现有代码,因为它仍然允许设置属性的各种模式:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();

user.Name = customer.Name; //*** No longer breaks internal data structures

user.Name = "string literal";  // Works as expected with implicit cast operator
user.Name = (string)customer.Name; // Still allowed with explicit/implicit cast operator
user.Name = customer.Name.Value; // Also works if Value property is still defined

因为这仍然不能回答最初的问题,所以如果在类属性上下文之外使用 Wrapper 类,即在对象之间传递等,使用 Wrapper 类仍然可能存在问题。也许通过适当的类设计可以消除整个 Wrapper 类,包括使用属性设置/获取访问器。

于 2014-09-09T18:44:52.120 回答
0

AJ Lane 我明白你的意思,我想你是对的——我只是想让图书馆的使用尽可能简单。

从 DbValue 到 T 的隐式转换的原因是简单地期望 T 的函数。

例如

literalSomething.Text = Server.HtmlEncode(SomeTable.SomeStringColumn);

而不是

literalSomething.Text = Server.HtmlEncode((string)SomeTable.SomeStringColumn);

这要求强制转换是隐式的。

话虽这么说,我只是在输入此内容时阅读了您的评论,我可以看到这确实是个问题。

我想我会回到通过属性来公开价值,它只需要开发人员输入更多,而且我认为代码有点难看。

想象一下 DbValue:

if (someValue.Value.HasValue) // someValue is DbValue<int?>

但是话又说回来,“丑陋”的代码可能比仅仅阅读它的行为与你所期望的不同的代码更好。

我想这个问题最终会成为一个“最佳实践”问题。

总而言之,我将创建一个 Value 属性并使用它而不是隐式强制转换,使用该库的开发人员将不得不忍受它。

感谢您的投入:-)

于 2009-03-14T14:09:08.300 回答
0

这就是属性的用途。它们允许您定义分配的含义。您不能为类或结构本身定义它,因为它们已经由语言定义来做必要的事情。只需在类中添加一个Value属性。

或者,编辑您的问题以更广泛地描述您的设计以及此 Wrapper 如何适应它,因为有人可能会建议一种更简单的方法。

于 2009-03-14T10:15:58.867 回答
0

我只是研究了一下,将类设为结构确实不是一种选择,因为它在无参数构造函数中有一些逻辑,而且它继承了一个抽象类,其中包含内部抽象函数。

我不能使用接口,因为这会使这些功能公开,这会完全破坏逻辑。

如果这会有所帮助,我可以发布整个课程,但它有点长(130 行)或者我可以在单独的服务器上折腾,如果这样会更好?(虽然它损害了这个问题的完整性,因为我最终可能会从该服务器中删除它)

还要解释这门课真的很难,没有写一篇完整的论文:-/

无论如何,我会尝试说明我遇到的问题。

假设 2 个表类:CustomerTable 和 UserTable:

public class CustomerTable
{
  Wrapper<string> Name;
}

public class UserTable
{
  Wrapper<string> Name;
}

现在的问题是其他一些开发人员可能会使用上面的代码如下:

CustomerTable customer = new CustomerTable();
UserTable user = new UserTable();
user.Name = customer.Name; // This breaks my internal dictionary

为了让它工作,开发人员应该做的是:

user.Name = (string)customer.Name;

然而,问题是,在编写代码时,有谁会在他们的头脑中想到这一点?

即使我使用了 Value 属性,开发人员仍然必须记住要写

user.Name = customer.Name.Value; // or user.Name.Value = ....

再一次,开发人员可能会忘记这一点,突然间他会遇到异常,或者更糟:没有持久化到数据库中的数据。

所以我的问题实际上是,我希望包装器完全透明(它应该可以使用,就好像它实际上是它正在包装的类/基元一样)。但是,当从一个包装器分配到另一个包装器时,我的内部逻辑会中断。

大量的写作和大量的代码 - 如果我过度写作,请告诉我。

于 2009-03-14T12:30:56.353 回答