我有一个有点简单的包装类的问题。
它看起来像这样:
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对象的指针的字典,所以我不能让它们一直在改变,因为字典会停止匹配。
我可以看出这有点令人困惑,所以如果我遗漏了任何重要的东西,请随时问: - )
答案 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访问器。