我们知道int是一个值类型,因此以下内容是有道理的:
int x = 3;
int y = x;
y = 5;
Console.WriteLine(x); //says 3.
现在,这里有一些代码,我们想要缺少一个更好的术语“链接”,这两个变量指向相同的内存位置。
int x = 3;
var y = MagicUtilClass.linkVariable(() => x);
y.Value = 5;
Console.WriteLine(x) //says 5.
问题是:linkVariable方法如何?它的返回类型是什么样的?
虽然我将帖子标题为使值类型表现为引用类型,但所述linkVariable方法也适用于引用类型..,即
Person x = new Person { Name = "Foo" };
var y = MagicUtilClass.linkVariable(() => x);
y.Value = new Person { Name = "Bar" };
Console.WriteLine(x.Name) //says Bar.
我不知道如何在C#中实现这一点(顺便说一下,不允许使用不安全的代码)?
欣赏想法。感谢。
答案 0 :(得分:2)
您可以这样做:
public delegate void Setter<T>(T newValue);
public delegate T Getter<T>();
public class MagicPointer<T>
{
private Getter<T> getter;
private Setter<T> setter;
public T Value
{
get
{
return getter();
}
set
{
setter(value);
}
}
public MagicPointer(Getter<T> getter, Setter<T> setter)
{
this.getter = getter;
this.setter = setter;
}
}
用法:
int foo = 3;
var pointer = new MagicPointer<int>(() => foo, x => foo = x);
pointer.Value++;
//now foo is 4
当然这个解决方案并不能保证强大的编译时控制,因为编写器可以编写一个好的getter或setter。
可能,如果你需要类似指针的东西,你应该重新考虑你的设计,因为你可以用C#中的另一种方式来做:)
答案 1 :(得分:2)
以下是完整的解决方案:
// Credits to digEmAll for the following code
public delegate void Setter<T>(T newValue);
public delegate T Getter<T>();
public class MagicPointer<T>
{
private Getter<T> getter;
private Setter<T> setter;
public T Value
{
get { return getter(); }
set { setter(value); }
}
public MagicPointer(Getter<T> getter, Setter<T> setter)
{
this.getter = getter;
this.setter = setter;
}
}
// Code starting from here is mine
public static class MagicUtilClass
{
public static MagicPointer<T> LinkVariable<T>(Expression<Func<T>> expression)
{
var memberExpr = expression.Body as MemberExpression;
if (memberExpr == null)
throw new InvalidOperationException("The body of the expression is expected to be a member-access expression.");
var field = memberExpr.Member as FieldInfo;
if (field == null)
throw new InvalidOperationException("The body of the expression is expected to be a member-access expression that accesses a field.");
var constant = memberExpr.Expression as ConstantExpression;
if (constant == null)
throw new InvalidOperationException("The body of the expression is expected to be a member-access expression that accesses a field on a constant expression.");
return new MagicPointer<T>(() => (T) field.GetValue(constant.Value),
x => field.SetValue(constant.Value, x));
}
}
<强>用法:强>
int x = 47;
var magic = MagicUtilClass.LinkVariable(() => x);
magic.Value = 48;
Console.WriteLine(x); // Outputs 48
要了解此解决方案的工作原理,您需要知道,只要在lambda表达式中使用变量,编译器就会相当大地转换代码(无论该lambda表达式是否成为委托或表达树)。它实际上生成一个包含字段的新类。删除变量 x 并替换为该字段。用法示例将如下所示:
CompilerGeneratedClass1 locals = new CompilerGeneratedClass1();
locals.x = 47;
var magic = MagicUtilClass.LinkVariable(() => locals.x);
// etc.
代码检索的“字段”是包含 x 的字段,它检索的“常量”是 locals 实例。
答案 2 :(得分:0)
.NET中的整数是不可变的。我不确定你试图用这个问题解决什么问题。您是否考虑过创建一个具有“包装”整数的属性的类?该类将是一个引用类型,你想要实现的不需要任何“魔术”实用程序类 - 只是正常的C#引用类型行为。