有没有办法创建托管类型变量的传递引用列表?
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);
编辑;根据反馈,我可能不清楚我的最终目标。我已经更新了最后一部分。
情况
我有许多变量需要对其执行相同的操作。在这种情况下,它们是 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);