有没有办法创建托管类型变量的传递引用列表?

Is there a way to create a Pass-By-Reference list of managed-type variables?

编辑;根据反馈,我可能不清楚我的最终目标。我已经更新了最后一部分。

情况

我有许多变量需要对其执行相同的操作。在这种情况下,它们是 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 或具有实际值。

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

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

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

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

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;
    }
}

更新:

现在我明白你想要做什么了。你有一堆变量,你想在不改变任何其他东西的情况下对它们执行某种批量操作。将它们放在 class 中不是一种选择。

在那种情况下,您真的只能一次一个地对它们进行操作。有多种方法可以缩短它,但您几乎无法重复。

我会

  • 创建一个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;
}

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

ref 并没有真正考虑在内。我们不需要经常使用它,如果有的话。使用值类型或 string 我们可以 return 来自函数的新值。

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

我会尝试在不使用 ref 的情况下先编写方法,并且仅在需要时才使用它。你可能永远不会,因为你会在不使用 ref.

的情况下成功地完成任何你想做的事情

您的评论提到这是一个遗留应用程序,最好不要修改现有的 class。这就留下了另一种选择——反思。不是我最喜欢的,但是当你说 "legacy app" 时,我感受到了你的痛苦。在那种情况下,您可以这样做:

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);

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

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

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