【问题标题】:C# Array of references to Variables and or Form objects对变量和/或表单对象的引用的 C# 数组
【发布时间】:2013-06-19 12:24:39
【问题描述】:

我想要做的是有一个对变量的引用数组。我的意思是等效于指向整数的 C 指针数组(例如)。

示例:(!!不是真正的代码!!)

int a = 4;
int b = 5;
int c = 6;
List<ref int> tmp = new List<ref int>();

tmp.Add(ref a);
tmp.Add(ref b);
tmp.Add(ref c);

tmp[0] = 16;
tmp[1] = 3;
tmp[2] = 1000;

Console.Writeline(tmp[0] + " " + a); // 16 16
Console.Writeline(tmp[1] + " " + b); // 3 3
Console.Writeline(tmp[2] + " " + c); // 1000 1000

我的案例的细节:我有一个字符串列表,它们将对应于字典中的键。我想我想要的是一个元组列表,其中 Type1 是对 int 或字符串的引用,而 Type2 是对文本框的引用。

我将遍历这个列表,使用字符串从字典中获取值(并对这些数据进行处理),然后将结果存储到 Type1 中。最终我将从那些 Type1 变量引用中获取数据并将它们的数据复制到相应的 Type2 文本框。

这就是我想我想做的事情的要点。如果有人认为我的方法过于复杂,我会说我需要保留文本框,因为它们是可悲的,所以我不能只创建一个数组并遍历它。最好也将 Type1 变量保持分开,尽管不是很有必要。

现在,通过阅读,我认为 Func 看起来是最接近我想要的有用的东西,所以我尝试使用以下(使用 Type1,作为对象,因为它需要处理两个整数和字符串)

List<Tuple<string, Func<object>, Func<object>>>

但我不确定如何使用它来获取对变量的引用。

【问题讨论】:

  • “如果有人认为我的方法过于复杂,我会说我需要保留文本框,因为它们是可悲的,所以我不能只创建一个数组并遍历它。” - 这是什么意思?
  • 你可以在这种情况下使用指针,在方法中使用 unsafe 关键字并设置项目不安全以允许在 c# 中使用指针,也可以将值封装在一个类中,在 C# 中每个类都是引用类型
  • @Ahmedsafan:虽然我不同意你建议的可取性,但这应该是一个答案,而不是评论。
  • 不确定我是否很好地理解了您的问题,但您可以使用泛型检查 link 。在执行异步或延迟编程模型之前,Func 委托并不是真正必要的
  • 好的,无论如何,我试图为技术问题提供解决方案,无论它的用途如何。

标签: c# arrays reference func


【解决方案1】:

特别要求的东西是不可能的;更合适的(并且有实际工作的便利!)是围绕你想要做的事情设计一个类结构。

例如,像这样的:

public class MyObject // Name it appropriately
{
    public object Value { get; set; }
    public string Key { get; set; }
    public TextBox TextBox { get; set; }
}

然后,在您的代码中,您可以执行类似的操作...

Dictionary<string, object> values = ...
List<MyObject> objects = ...

foreach(var item in objects)
{
    item.Value = values[item.Key];

    // process your data

    item.TextBox = item.Value.ToString();
}

显然,这只是一个粗略的设计,这里的类只不过是一个数据传输容器。例如,您可以通过使用Value 属性的设置器自动设置TextBox 的值来使类“更智能”。但这应该可以让您大致了解如何以 OO 方式完成此类事情。

编辑这是您的示例的外观。

MyObject a = new MyObject() { Value = 4 };
MyObject b = new MyObject() { Value = 5 };
MyObject c = new MyObject() { Value = 6 };
List<MyObject> tmp = new List<MyObject>();

tmp.Add(a);
tmp.Add(b);
tmp.Add(c);

tmp[0].Value = 16;
tmp[1].Value = 3;
tmp[2].Value = 1000;

Console.Writeline(tmp[0].Value.ToString() + " " + a.Value.ToString()); // 16 16
Console.Writeline(tmp[1].Value.ToString() + " " + b.Value.ToString()); // 3 3
Console.Writeline(tmp[2].Value.ToString() + " " + c.Value.ToString()); // 1000 1000

【讨论】:

  • 如果当我创建一个 MyObject 并将文本框设置为表单上现有的 Texbox 时,然后将 textbox.Text 属性设置为某个值,并更新表单上的任何内容,那么这是正是我想要的。如果没有,我看不出这有什么帮助
  • @DanielCardin:是的,它会的。由于TextBox 是一个类(因此是一个引用类型),因此更改属性或任何其他特定于实例的信息将在程序中有权访问此实例的任何位置可见。换句话说,它会做你想做的事。您可能会发现 this 很有趣。虽然它专门针对参数传递,但其中的信息适用于所有变量。
  • 同样适用于 Value 只是因为它在这个类中?
  • 我不知道我是不是做错了,但我试过了,它正确地设置了我想要的 texbox.text,但它似乎并没有改变我输入的内容价值。与 new MyObject("test", existingInt, existingTextbox); 之类的含义相同,它会更改 existingTextbox,但打印出 existingInt 不会是我将 Value 更改为的内容。
  • +1 如果您只需要担心更改引用类型的属性,我会使用这种方法。
【解决方案2】:

您不能使用 C# 存储引用。调用方法时只能使用ref关键字。

可以使用指针,但您只能使用fixed 表达式和unsafe 上下文来执行此操作。

可以使用委托来伪造这种东西,但我不确定它是否是你要找的东西。我也相当确定您确实需要重新设计您的方法,但是,这里有一个如何伪造它的示例...

首先,像这样编写一个“值包装器”类:

public class ValueWrapper<T>
{
    readonly Func<T>   get;
    readonly Action<T> set;

    public ValueWrapper(Func<T> get, Action<T> set)
    {
        this.get = get;
        this.set = set;
    }

    public T Value
    {
        get
        {
            return get();
        }

        set
        {
            set(value);
        }
    }
}

然后你可以用它来改变值:

void run()
{
    int x = 0;

    var intWrapper = new ValueWrapper<int>(() => x, value => x = value);

    test(intWrapper);

    Console.WriteLine(x);  // Prints 42, which shows that x was changed.

    TextBox textBox = new TextBox {Text = ""};

    var stringWrapper = new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);

    test(stringWrapper);

    Console.WriteLine(textBox.Text); // Prints "Changed".
}

static void test(ValueWrapper<int> wrapper)
{
    wrapper.Value = 42;
}

static void test(ValueWrapper<string> wrapper)
{
    wrapper.Value = "Changed";
}

您还可以在一个方法中创建包装器并将其传递给使用包装器更改原始包装对象中的属性的不同方法,如下所示:

void run()
{
    TextBox textBox = new TextBox {Text = ""};

    var wrapper = test1(textBox);
    test2(wrapper);
    Console.WriteLine(textBox.Text); // Prints "Changed"
}

void test2(ValueWrapper<string> wrapper)
{
    wrapper.Value = "Changed";
}

ValueWrapper<string> test1(TextBox textBox)
{
    return new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
}

警告:这确实会导致一些相当令人头疼的代码,例如:

void run()
{
    var intWrapper = test();
    intWrapper.Value = 42;
    Console.WriteLine(intWrapper.Value); // Works, but where is the value? It can't be the x inside test()!
}

ValueWrapper<int> test()
{
    int x = 0;
    var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
    return intWrapper;
}

所以我们从test() 返回了一个ValueWrapper,它显然是从test() 内部包装了一个局部变量。然后我们显然可以更改值并将其打印出来......

当然,这并不是真正发生的事情,但它可能会非常令人困惑!

【讨论】:

  • +1,虽然我很好奇为什么 getset 代表(而不是仅仅将值存储在实例变量中)
  • @AdamRobinson 因为我特别想改变原来的值,即test()方法中栈上x的值。请注意,当我们调用 test() 方法时,x 的值实际上是如何变化的,这使得它看起来就好像在 test() 中以某种方式使用了对 x 的引用。捕获变量的魔力。 :)
  • 我知道闭包可以做什么,但考虑到它实际上(实际上)在做同样的事情(创建一个类来容纳局部变量,然后在之后替换局部变量的每次访问可以访问该类实例上的属性的闭包)但没有委托调用的开销,我并没有真正看到优势。事实上,没有魔法:)
  • @AdamRobinson 我的意思是,如果您查看Console.WriteLine(x); 行,它直接使用x不是 值包装器。您可以看到x 已更改。我特别 not “用对属性的访问替换每个访问”(我认为您的意思是使用包装器而不是原始值)。包装器所做的更改对于使用原始值的代码是可见的,这很重要。
  • 在您的代码中,是的,但实际上并非如此。查看有关闭包和在匿名委托中捕获变量的可用文档。这些特性完全是语法糖;它们实际上并没有在声明函数之外扩展实例变量的范围或可见性(因为这是不可能的)。或者,更好的是,看看 IL。你会发现 IL 完全按照我的描述进行。
【解决方案3】:

在这种情况下你可以使用指针,在方法中使用 unsafe 关键字并设置项目不安全以允许在 c# 中使用指针,也可以将值封装在一个类中,在 C# 中每个类都是引用类型

【讨论】:

    【解决方案4】:

    我用过这个并且效果很好:

    exp.

    public int value1 = 3;
    public int value2 = 4;
    public int value3 = 5;
    
    public void Method1()
    {
        int[] values = { value1, value2, value3};
        for (int i = 0; i < values.Length; i ++)
        {
            Console.WriteLine(values[i]);
        }
    }
    

    【讨论】:

    • Labels 等表单对象也可以做到这一点。
    猜你喜欢
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-19
    • 2015-10-01
    • 2020-06-19
    相关资源
    最近更新 更多