C# 模仿重写赋值运算符 (=)

发布于 2024-07-15 05:36:02 字数 924 浏览 10 评论 0原文

我的一个简单的包装类遇到了一些问题。

它看起来像这样:

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 的隐式转换器,因此它的行为几乎就像 T 本身的实例一样。

例如,

Wrapper<int> foo = 42;

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

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

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

或者通过属性公开公开 _value 。

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

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

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

I've got a bit of a problem with a somewhat simple wrapper class I have.

It looks something like this:

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;
  }
}

I've overriden the implicit converters from and to T, so it behaves almost like an instance of T itself.

e.g.

Wrapper<int> foo = 42;

However I've got a slight problem when assigning one instance of Wrapper to another, since I only want to assign the value of the second Wrapper class.

So right now, I have to do this:

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

Or expose _value publicly through a property.

However since this is in a library, and I don't want the user to depend on remembering this, do you guys have any idea how I could mimic overridding the assignment operator ?

The problem in just changing the pointer (as it does when assigning a class instance to another), is that I've got a dictionary of pointers to these Wrapper objects, so I cannot have them changing all the time, since the dictionary would stop matching then.

I can see if this is somewhat confusing, so if I've left anything important out, please feel free to ask :-)

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

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

发布评论

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

评论(8

探春 2024-07-22 05:36:02

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

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

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

您可以发挥真正的创意并重载某些运算符,使其基本上不执行任何操作。 但我不建议这样做。 对此类事情使用运算符重载通常会使代码变得神秘并且经常会崩溃。

Since the assignment operator can't be overloaded, there isn't a real good solution. As somebody else pointed out, using a struct will give you the assignment semantics that you want, but then you're faced with value semantics--often not a good thing.

One option is to overload the constructor:

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

Which would result in this syntax:

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

Although more verbose than what you have, it reads better.

Or you could add a Clone method (not the ICloneable interface), so that you could write:

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

You could get really creative and overload some operator, making it do essentially nothing. I wouldn't recommend that, though. Using operator overloading for those kinds of things typically makes code cryptic and often breaks.

你如我软肋 2024-07-22 05:36:02

你可以让 Wrapper一个结构。 但是我不确定这是否适合您的应用程序设计。

You could make Wrapper<T> a struct. However I'm not sure if this would suit your application design or not.

゛时过境迁 2024-07-22 05:36:02

如果您查看 Nullable...它的作用与您在这里所做的非常相似,它使用 .Value 属性公开内部值。

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

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

If you look at Nullable<T>...which does a very similar thing to what you are doing here, it exposes the internal value using a .Value property.

The problem in just changing the pointer (as it does when assigning a class instance to another), is that I've got a dictionary of pointers to these Wrapper objects, so I cannot have them changing all the time, since the dictionary would stop matching then.

I'm not sure I follow this, what exactly are you storing in the dictionary? Because if you are storing references, the CLR will update them as necessary.

裂开嘴轻声笑有多痛 2024-07-22 05:36:02

不要隐式地双向转换你的包装器。

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 是一种有损转换(至少,您会丢失它是来自数据库的值这一事实),并且根据最佳实践应该明确一点。 如果从 DBValue 转换为 T 不会丢失任何内容,那么您不妨只使用返回 T 的属性。

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

要求下游开发人员编写:

string value = MyProperty.Value

string value = (string)MyProperty

代替

string value = MyProperty

... 并不是那么繁琐,并且可以确保每个人都确切知道发生了什么。

编辑:

要真正回答问题,您不能覆盖参考分配 - 或使其看起来像您已经这样做 - 但您实际上不需要这样做。

Don't implicitly cast your wrapper both ways.

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;
    }
}

Casting from DBValue<T> to T is a lossy conversion (as a minimum, you lose the fact that it's a value from the database), and by best-practice should be explicit. If you don't lose anything by casting from DBValue<T> to T, you might as well just use properties that return T.

Basically, you've already seen why you shouldn't be trying to do this: if a DBValue can be substituted for T and the other way around, how does the compiler (or developer) know which one to choose?

Requiring down-stream developers to write:

string value = MyProperty.Value

or

string value = (string)MyProperty

instead of

string value = MyProperty

...isn't all that onerous, and makes sure that everyone knows exactly what's going on.

EDIT:

To actually answer the question, you can't override reference assignment - or make it look like you have - but you shouldn't really need to.

埋葬我深情 2024-07-22 05:36:02

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

或者,编辑您的问题以更广泛地描述您的设计以及此包装器如何融入其中,因为有人可能会建议一种更简单的方法。

This is what properties are for. They allow you to define what assignment means. You can't define it for a class or struct itself because they are already defined by the language to do necessary things. Just add a Value property to the class.

Alternatively, edit your question to give a broader description of your design and how this Wrapper fits into it, as someone may be able to suggest a simpler approach.

緦唸λ蓇 2024-07-22 05:36:02

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

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

如果有帮助的话我可以发布整个课程,但是有点长(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 = ....

开发人员可能会再次忘记这一点,并且突然他会遇到异常,或者更糟:数据未持久保存到数据库中。

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

唷,写了很多,还有很多代码——如果我写得太过头了,请告诉我。

I just looked into it, making the class a struct is really not an option, since it has some logic in the parameterless constructor, plus it inherits an abstract class, which contains internal abstract functions.

I cannot use an interface, as that'd make those functions public, which would break the logic entirely.

I can post the entire class if that'd be helpful, but it's somewhat long (130 lines)
Or I could toss up on a seperate server, if that'd be better ? (though it hurts the integrity of this question, as I may delete it eventually from that server)

Also explaining the class is really difficult, without writing a complete essay :-/

Anyway I'll try to illustrate the problem I'm having.

Assume 2 table classes: CustomerTable and UserTable:

public class CustomerTable
{
  Wrapper<string> Name;
}

public class UserTable
{
  Wrapper<string> Name;
}

Now the problem is that some other developer, may use the above code as follows:

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

What the developer should had done, in order for it to work, was:

user.Name = (string)customer.Name;

The problem is however, who in their right mind would think about that, when writing code ?

Even if I used a Value property, the developer would still have to remember to write

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

And again the developer may forget this, and all of a sudden he gets exceptions, or worse: data which isn't persisted to the database.

So my issue is really, that I want the wrapper to be completely transparent (it should be usable as if it was in fact the class/primitive it's wrapping).
However when assigning from one wrapper to another, my internal logic breaks.

Phew a lot of writing, and a lot of code - let me know if I overdo the writing.

ぃ弥猫深巷。 2024-07-22 05:36:02

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 属性并使用它来代替隐式转换,而使用该库的开发人员将不得不忍受这一点。

感谢您的投入:-)

A J Lane I see what you mean, and I guess you're right - I just wanted to make it as simple as possible to use the library.

The reason for the implicit cast from DbValue to T, is to simply functions which expects T.

for example

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

rather than

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

This requires the cast to be implicit.

That being said I just read your comment whilst typing this, and I can see that's quite the issue.

I think I'll go back to exposing value through a property, it just requires the developer to type more, and kinda makes the code ugly I think.

Just imagine DbValue:

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

But then again it's probably better with "ugly" code, than code which behaves differently from what you'd expect by merely reading it.

I guess this question ends up as a "best practice" question really.

So to conclude, I'll create a Value property and use that instead of implicit casts, and the developer using the library will just have to live with that.

Thanks for your inputs :-)

温柔少女心 2024-07-22 05:36:02

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


甚至可以保留原始给定的Wrapper类及其隐式运算符,这里的代码可以工作:

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 类如果它在类属性上下文之外使用,即在对象之间传递,等等,就会出现问题。也许可以通过正确的类设计(包括使用属性设置/获取访问器)来消除整个包装类。

This old post stills needs additional information to be complete. It's apparent that the original desired behavior cannot be accomplished since the = operator cannot be overloaded, and likewise C# cannot be "tricked" into casting an object to its own type... it will always boil down to a class reference assignment. But Steffen's further posts show the Wrapper class being used not just with local variables, but as a class field type. The desired semantics can be used AND the integrity of the internal dictionary maintained by using class properties instead of public fields.


Even keeping the original given Wrapper<T> class with both its implicit operators, here's code that would work:

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; }
    }
}

If this change were made, it would not break existing code since it still allows various modes of setting the property:

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

Because this still doesn't answer the original question, use of the Wrapper class could still be problematic if its used outside the class property context, i.e. passed between object, etc. Perhaps the entire Wrapper class could be eliminated with the proper class design, including use of property set/get accessors.

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