C# 模仿覆盖赋值运算符 (=)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/644854/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
C# mimic overridding the assignment operator (=)
提问by
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.
我已经覆盖了从和到 T 的隐式转换器,所以它的行为几乎就像 T 本身的一个实例。
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.
但是,在将 Wrapper 的一个实例分配给另一个实例时,我遇到了一个小问题,因为我只想分配第二个 Wrapper 类的值。
So right now, I have to do this:
所以现在,我必须这样做:
Wrapper<int> foo = 42;
Wrapper<int> bar = (int)foo;
Or expose _value publicly through a property.
或者通过属性公开 _value。
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.
仅更改指针的问题(就像将一个类实例分配给另一个实例时一样),是我有一个指向这些 Wrapper 对象的指针的字典,所以我不能让它们一直改变,因为字典会停止那么匹配。
I can see if this is somewhat confusing, so if I've left anything important out, please feel free to ask :-)
我可以看到这是否有点令人困惑,所以如果我遗漏了任何重要的内容,请随时提问:-)
回答by FlySwat
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.
如果您查看 Nullable<T>... 它与您在这里所做的非常相似,它使用 .Value 属性公开内部值。
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.
仅更改指针的问题(就像将一个类实例分配给另一个实例时一样),是我有一个指向这些 Wrapper 对象的指针的字典,所以我不能让它们一直改变,因为字典会停止那么匹配。
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.
我不确定我是否遵循了这一点,您在字典中究竟存储了什么?因为如果您要存储引用,CLR 将根据需要更新它们。
回答by zildjohn01
You could make Wrapper<T> a struct
. However I'm not sure if this would suit your application design or not.
你可以让 Wrapper<T> a struct
。但是我不确定这是否适合您的应用程序设计。
回答by Jim Mischel
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:
或者您可以添加一个Clone
方法(而不是ICloneable
接口),以便您可以编写:
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.
你可以变得非常有创意并重载一些运算符,使其基本上什么都不做。不过,我不建议这样做。对这些类型的事情使用运算符重载通常会使代码变得神秘并且经常中断。
回答by Daniel Earwicker
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.
这就是属性的用途。它们允许您定义分配的含义。您不能为类或结构本身定义它,因为语言已经定义了它们来做必要的事情。只需Value
在类中添加一个属性即可。
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.
或者,编辑您的问题以更广泛地描述您的设计以及此 Wrapper 如何融入其中,因为有人可能会建议一种更简单的方法。
回答by Daniel Earwicker
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)
如果有帮助,我可以发布整个课程,但它有点长(130 行)或者我可以在单独的服务器上折腾,如果这样会更好?(虽然它损害了这个问题的完整性,因为我最终可能会从该服务器中删除它)
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:
假设有 2 个表类:CustomerTable 和 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
即使我使用了 Value 属性,开发人员仍然必须记住写
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.
呼,写了很多,还有很多代码——如果我写得太多,请告诉我。
回答by ajlane
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
.
从DBValue<T>
toT
转换是一种有损转换(至少,您会丢失它是来自数据库的值的事实),并且最佳实践应该是明确的。如果从DBValue<T>
to转换不会丢失任何东西T
,那么您不妨只使用 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?
基本上,您已经看到为什么不应该尝试这样做:如果可以用 DBValue 代替 T,反之亦然,编译器(或开发人员)如何知道选择哪个?
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.
要真正回答这个问题,您不能覆盖引用分配 - 或者让它看起来像您拥有的 - 但您不应该真的需要。
回答by ajlane
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.
AJ Lane 我明白你的意思,我想你是对的 - 我只是想让使用这个库尽可能简单。
The reason for the implicit cast from DbValue to T, is to simply functions which expects T.
从 DbValue 到 T 的隐式转换的原因是简单的函数需要 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:
想象一下 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.
所以总而言之,我将创建一个 Value 属性并使用它而不是隐式强制转换,而使用该库的开发人员将不得不接受它。
Thanks for your inputs :-)
感谢您的投入:-)
回答by C Perkins
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.
这篇旧帖子仍然需要额外的信息才能完成。很明显,最初期望的行为无法实现,因为 = 运算符不能被重载,同样,C# 不能被“欺骗”将对象转换为它自己的类型......它总是归结为类引用分配。但是 Steffen 的进一步帖子显示 Wrapper 类不仅用于局部变量,还用作类字段 type。 可以使用所需的语义,并通过使用类属性而不是公共字段来维护内部字典的完整性。
Even keeping the original given Wrapper<T>
class with both its implicit operators, here's code that would work:
即使保留原始给定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; }
}
}
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.
因为这仍然不能回答最初的问题,所以如果在类属性上下文之外使用 Wrapper 类仍然可能有问题,即在对象之间传递等。也许可以通过适当的类设计消除整个 Wrapper 类,包括使用属性设置/获取访问器。