C#模仿覆盖赋值运算符(=)

时间:2009-03-13 22:34:42

标签: c# operator-overloading variable-assignment

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

它看起来像这样:

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本身的一个实例。

e.g。

Wrapper<int> foo = 42;

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

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

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

或通过财产公开公开_value。

然而,由于这是在一个库中,我不希望用户依赖于记住这个,你们有没有想过我怎么能模仿覆盖赋值运算符?

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

我可以看出这有点令人困惑,所以如果我遗漏了任何重要的东西,请随时问: - )

8 个答案:

答案 0 :(得分:5)

由于赋值运算符不能重载,因此没有一个真正好的解决方案。正如其他人所指出的那样,使用struct会给你你想要的赋值语义,但是你会遇到价值语义 - 通常不是一件好事。

一个选项是重载构造函数:

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();

你可以变得非常有创意并让一些操作员超载,这使得它基本上什么都不做。不过,我不建议这样做。使用运算符重载来处理这些事情通常会使代码变得神秘并经常中断。

答案 1 :(得分:3)

你可以制作Wrapper&lt; T&gt;一个struct。但是我不确定这是否适合您的应用程序设计。

答案 2 :(得分:1)

如果你看看Nullable&lt; T&gt; ...它与你在这里做的非常相似,它会使用.Value属性公开内部值。

  

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

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

答案 3 :(得分: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;
    }
}

DBValue<T>转换为T是一种有损转换(至少,您失去了它是数据库中的值的事实),并且最佳实践应该是明确的。如果您从DBValue<T>转换为T并没有丢失任何内容,那么您也可以使用返回T的属性。

基本上,您已经看到了为什么不应该尝试这样做:如果DBValue可以替代T而反过来,编译器(或开发人员)如何知道选择哪一个? / p>

要求下游开发人员写:

string value = MyProperty.Value

string value = (string)MyProperty

而不是

string value = MyProperty

......并不是那么繁重,并确保每个人都知道究竟发生了什么。

修改

要真正回答这个问题,你不能覆盖参考作业 - 或者让它看起来像你一样 - 但你真的不需要。

答案 4 :(得分:0)

这是属性的用途。它们允许您定义分配的含义。您无法为类或结构本身定义它,因为它们已由语言定义以执行必要的操作。只需在课程中添加Value属性即可。

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

答案 5 :(得分:0)

我只是调查它,使类成为一个结构实际上不是一个选项,因为它在无参数构造函数中有一些逻辑,而且它继承了一个包含内部抽象函数的抽象类。

我无法使用界面,因为这会使这些功能公开,这会完全破坏逻辑。

如果有帮助的话,我可以发布全班,但它有点长(130行) 或者我可以折腾一个单独的服务器,如果那更好? (虽然它会伤害这个问题的完整性,因为我最终可能会从该服务器中删除它)

如果没有写完整的论文,那么解释这门课真的很难: - /

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

假设有两个表类: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 = ....

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

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

写了很多文章和很多代码 - 如果我过度写作,请告诉我。

答案 6 :(得分:0)

A J 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属性并使用它而不是隐式强制转换,使用该库的开发人员将不得不忍受它。

感谢您的意见: - )

答案 7 :(得分:0)

此旧帖子仍然需要完整的其他信息。很明显,原始的期望行为无法实现,因为=运算符不能过载,同样C#也不能被欺骗&#34;将对象转换为自己的类型...它总是归结为类引用赋值。但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类可以用正确的类设计,包括使用属性集/ get访问器。