【问题标题】:Is there a way to create a Pass-By-Reference list of managed-type variables?有没有办法创建托管类型变量的传递引用列表?
【发布时间】:2020-04-13 17:57:26
【问题描述】:

编辑;根据回复,我可能不清楚我的最终目标。我已经更新了最后一节。

情况

我有许多变量需要对它们执行相同的操作。在这种情况下,它们是strings,并且在我们到达此代码时可以具有值null"""Blank",或者它们可能已经具有我想要保留的分配的其他值。

if (String.IsNullOrEmpty(MyVar1) || "Blank".Equals(MyVar1))
    MyVar1 = null;
if(String.IsNullOrEmpty(MyVar2) || "Blank".Equals(MyVar2))
    MyVar2 = null;
...
if(String.IsNullOrEmpty(MyVar10) || "Blank".Equals(MyVar10))
    MyVar10 = null;

作为一个想要保持我的代码干净的程序员,这个块让我发疯,我正在寻找一种方法来创建这些变量的列表,并对每个变量执行相同的 if 语句 + 空赋值。

例如,这是我想做的:

MyVar1 = "Blank";
DreamDataStructure varList = new DreamDataStructure() { MyVar1, MyVar2, ..., MyVar10 };
foreach(ref string MyVar in varList)
{
    if(String.IsNullOrEmpty(MyVar) || "Blank".Equals(MyVar))
        MyVar = null;
}
Console.WriteLine(MyVar1); //Should now be null

什么不起作用

1) 因为我的变量是strings,所以我不能这样做。

var myListOfVariables = new[] { &MyVar1, &MyVar2, ..., &MyVar10 };

如果可以,我可以按预期通过foreach 处理它们。因为string 是托管类型,所以不能像这样通过引用传递。

2) 同样,如果我只是为变量创建了一个List<string>,它们将按值传递,对我的情况没有帮助。

3) 这些变量不能包装在外部对象类型中,因为它们需要在遗留应用程序的很多地方用作字符串。假设要更改它们在每个位置的使用方式将付出很大的努力。

问题

有没有办法以传递引用的方式迭代string(或其他托管类型)变量,这将允许我将整个操作放入循环中并减少此处发生的代码重复?

这里的目标是我可以稍后在我的代码中使用 原始变量 来更新值。 MyVar1 等稍后已被旧代码引用,这些代码期望它们为 null 或具有实际值。

【问题讨论】:

  • “这些变量不能包装在外部对象类型中,因为它们需要在遗留应用程序的很多地方用作字符串。”您接受 just 用于此目的的外部对象类型,其中外部对象类型设置变量?
  • 现在我明白你想要做什么了。 DreamDataStructure 看起来这些已经是类的属性,但它们不是。它们只是变量。如果您修改代码以使这些变量成为类的属性,这将变得很容易。但是,如果你不能——你所拥有的只是个别变量并且你被困住了——那么你一次只能修改一个。没有别的办法。

标签: c# data-structures pass-by-reference


【解决方案1】:

如果我正确理解您的问题,我认为您想要做的事情是不可能的。请看这个问题:Interesting "params of ref" feature, any workarounds?

我唯一能建议的(我知道这不能回答你的问题)是创建一种方法来避免重复你的条件逻辑:

void Convert(ref string text)
{
    if (string.IsNullOrEmpty(text) || "Blank".Equals(text))
    {
        text = null;
    }
}

【讨论】:

  • 这是一个非常有趣的链接,很可能表明这是不可能的。如果情况确实如此,我会阅读并接受这个答案。谢谢!
【解决方案2】:

您可以创建一个函数而不是传递引用,这也将更具可读性。

string Validate(string inputString)
{
   return string.IsNullOrEmpty(inputString) || "Blank".Equals(inputString) ? null : inputString;
}

<...>
 MyVar1 = Validate(MyVar1);

【讨论】:

  • 是的,但我正在寻找可扩展的解决方案。如果有 100 个变量,我仍然需要为每个变量调用 validate(这是我想要避免的)
【解决方案3】:

更新:

现在我明白你想要做什么了。你有一堆变量,你想对它们执行某种批量操作而不改变其他任何东西。把他们放在一个班级里不是一个选择。

在这种情况下,您真的只能一次只对它们进行操作。有一些方法可以缩短它,但你几乎被重复所困。

我愿意

  • 创建一个string SanitizeString(string input) 函数
  • 为每个变量输入一次x = SanitizeString(x);
  • 复制并粘贴变量名称以替换x

这很蹩脚,但仅此而已。


也许这是一个更好的方法。它确保始终对值进行清理。否则,您无法轻易判断这些值是否已被清理:

public class MyValues
{
    private string _value1;
    private string _value2;
    private string _value3;

    public string Value1
    {
        get { return _value1; }
        set { _value1 = Sanitize(value); }
    }

    // repeat for other values

    private string Sanitize(string input) =>
        string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
}

这是一种选择。另一个是更早地清理输入。但理想情况下,我们希望确保给定的类始终处于有效状态。无论值是否有效,我们都不想拥有一个类的实例。最好确保它们始终有效。

ref 并没有真正考虑到它。如果有的话,我们不需要经常使用它。使用值类型或string,我们可以从函数中返回一个新值。

如果我们正在传递一个引用类型并且我们想要对其进行更改(例如设置它的属性、将项目添加到列表中)那么我们已经在传递一个引用并且我们不需要指定 ref .

我会尝试先编写方法而不使用ref,并且仅在需要时使用它。您可能永远不会这样做,因为不使用ref,您将在尝试做的任何事情上取得成功。


您的评论提到这是一个遗留应用程序,最好不要修改现有类。这留下了另一种选择——反思。不是我最喜欢的,但当你说“旧版应用程序”时,我会感觉到你的痛苦。在这种情况下,您可以这样做:

public static class StringSanitizer
{
    private static Dictionary<Type, IEnumerable<PropertyInfo>> _stringProperties = new Dictionary<Type, IEnumerable<PropertyInfo>>();

    public static void SanitizeStringProperties<T>(T input) where T : class
    {
        if (!_stringProperties.ContainsKey(typeof(T)))
        {
            _stringProperties.Add(typeof(T), GetStringProperties(typeof(T)));
        }

        foreach (var property in _stringProperties[typeof(T)])
        {
            property.SetValue(input, Sanitize((string)property.GetValue(input)));
        }
    }

    private static string Sanitize(string input)
    {
        return string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
    }

    private static IEnumerable<PropertyInfo> GetStringProperties(Type type)
    {
        return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .Where(property => property.PropertyType == typeof(string) && property.CanRead && property.CanWrite);
    }
}

这将获取一个对象,找到它的字符串属性,并清理它们。它将按类型将字符串属性存储在字典中,这样一旦它发现了给定类型的字符串属性,就不必再这样做了。

StringSanitizer.SanitizeStringProperties(someObject);

【讨论】:

  • #3 on "what doesn't work" 解释了我对这种方法的问题。这是一个遗留应用程序,替换下游使用 MyVar1 等所需的工作是不可行的。相信我,我很想有一种不依赖字符串解析的方法。
  • 我提供了第二个选项,但我的第一个选项不会影响使用此类的现有代码。这才是重点。读取属性的代码不会改变。设置属性的代码也不会改变。类本身会在设置属性时对其进行清理。
  • 应用程序的其他区域仍然有像displayText = MyVar1 ?? 0 这样的调用,这就是导致问题的原因。我仍然需要 MyVar 包含 null 或有效文本,我不能用 MyNewTypeVar1 或 MyValues.Value1 替换 MyVar1。反射方法很有趣,但我必须做一些研究,看看我是否可以让它清理每个字符串变量,或者它是否只需要做一个子集。这是对我的特定场景的可能答案,但如果将来可能的话,我喜欢更通用的方法。
  • 我意识到我仍然不明白这个问题。您所描述的是我们为什么使用类的完美示例。如果你只有一堆变量,那么对它们进行操作的唯一方法就是一次一个。拥有一个具有属性的类可以让您执行所需的操作。
【解决方案4】:

您可以像这样简单地使用字符串 [] 并将更改返回给调用方方法。

public Main()
{
    var myVar1 = "Blank";
    var myVar2 = "";
    string myVar3 = null;
    var myVar4 = "";
    string[] dreamDataStructure = new string[] { myVar1, myVar2, myVar3, myVar4 };
}

private void ProcessStrings(string[] list)
{
    for(int i = 0; i < list.Length; i++)
    {

        if (String.IsNullOrEmpty(list[i]) || "Blank".Equals(list[i]))
            list[i] = null;
    }
}

【讨论】:

  • 我认为 OP 希望更新原始变量,而不是数组中的字符串。
猜你喜欢
  • 2011-11-13
  • 2020-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-10
  • 2011-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多