我正在尝试编写一个用数组内容填充字符串的函数,或者将它们设置为null。字符串的数量可能会有所不同,我不想添加像这些一样的要求都是同一个数组或类的一部分。
在C#中,您无法合并param
和out
。因此,执行此操作的唯一方法似乎是重载此方法:
public void ParseRemainders(string[] remainders, out string p1)
{
p1 = null;
if ((remainders != null) && (remainders.Length > 0))
p1 = remainders[0];
}
public void ParseRemainders(string[] remainders, out string p1, out string p2)
{
p1 = null;
p2 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1);
if (remainders.Length > 1)
p2 = remainders[1];
}
}
public void ParseRemainders(string[] remainders, out string p1, out string p2, out string p3)
{
p1 = null;
p2 = null;
p3 = null;
if (remainders != null)
{
ParseRemainders(remainders, out p1, out p2);
if (remainders.Length > 2)
p3 = remainders[2];
}
}
.... and on forever ....
如何避免所有这些代码重复,理想情况下接受任意数量的参数?
修改:这很有用,因为您可以执行ParseRemainders(remainders, out inputFileName, out outputFileName, out configFileName)
,然后避免手动执行
if (remainder.Length > 0) inputFileName = remainder[0];
if (remainder.Length > 1) outputFileName = remainder[1];
if (remainder.Length > 2) configFileName = remainder[2];
...
对不起,如果不清楚,我有一个特定的目标,我为什么不简单地返回List<>
。
结论:感谢BotondBalázs的回答,特别是暗示这被称为“阵列解构”。正如他们所指出的那样,并且正如这个问题所证实的那样,在当前版本的C#中是不可能的:Destructuring assignment - object properties to variables in C#
答案 0 :(得分:12)
到目前为止,我采取的方法与任何答案不同。
static class Extensions {
public static SafeArrayReader<T> MakeSafe<T>(this T[] items)
{
return new SafeArrayReader<T>(items);
}
}
struct SafeArrayReader<T>
{
private T[] items;
public SafeArrayReader(T[] items) { this.items = items; }
public T this[int index]
{
get
{
if (items == null || index < 0 || index >= items.Length)
return default(T);
return items[index];
}
}
}
在那里,现在你有一个数组,它给你一个默认值而不是抛出:
var remainder = GetRemainders().MakeSafe();
var input = remainder[0];
var output = remainder[1];
var config = remainder[2];
容易腻。您对数据类型的语义有问题吗? 制作一个更好的数据类型,封装所需的语义。
答案 1 :(得分:10)
如果我理解正确,您的用例将如下所示:
var remainders = new[] { "a", "b", "c" };
string a, b, c;
ParseRemainders(remainders, a, b, c); // after this, a == "a", b == "b" and c == "c"
您希望在C#中使用的功能称为数组解构,就像在JavaScript中一样:
var remainders = ["a", "b", "c"];
var [a, b, c] = remainders; // after this, a == "a", b == "b" and c == "c"
不幸的是,据我所知,
使用C#一般无法解决这个问题。
C#7虽然会有元组解构。
答案 2 :(得分:7)
嗯,您可以将方法更改为
public IEnumerable<string> ParseRemainders(string[] remainders)
{
var result = new List<string>();
///... your logic here, fill list with your strings according to your needs
return result;
}
答案 3 :(得分:6)
Andys方法很好,但我返回string[]
,因为它应该与输入数组具有相同的大小,如果输入数组是null
,则返回null
:
public string[] ParseRemainders(string[] remainders)
{
if(remainders == null) return null;
var parsed = new string[remainders.Length];
for(int i = 0; i < remainders.Length; i++)
parsed[i] = ParseRemainder(remainders[i]);
return parsed;
}
澄清ParseRemainder
(单个string
的不同方法)的作用:
public string ParseRemainder(string remainder)
{
// parsing logic here...
return "the parsing result of remainder";
}
答案 4 :(得分:3)
为了完整起见,这就是你在C#7(Visual Studio 2017)中做这种事情的方法:
string[] test = { "One", "Two", "Three", "Four", "Five" };
var (a, b, c) = (test[0], test[2], test[4]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == "Five");
这里的重要一行是var (a, b, c) = (test[0], test[2], test[4]);
,它向您展示了从数组的某些元素中分配几个不同变量的简写方法。
但是,如果数组不够长,则无法分配null。您可以通过编写辅助类来解决该问题:
public sealed class ElementsOrNull<T> where T: class
{
readonly IList<T> array;
public ElementsOrNull(IList<T> array)
{
this.array = array;
}
public T this[int index]
{
get
{
if (index < array.Count)
return array[index];
return null;
}
}
}
然后:
string[] test = { "One", "Two", "Three", "Four", "Five" };
var t = new ElementsOrNull<string>(test);
var (a, b, c) = (t[0], t[2], t[6]);
Debug.Assert(a == "One");
Debug.Assert(b == "Three");
Debug.Assert(c == null);
但我相信大多数人(包括我自己)会认为这比它的价值更麻烦。
答案 5 :(得分:3)
我认为这非常接近你想要的。它不需要C#7,适用于任何数据元素类型,并且不限于数组。但是,您可能希望选择比ValueReader / ReadValue更好的名称。
static class Extensions
{
public static ValueReader<T> ReadValue<T>(this IEnumerable<T> source, out T value)
{
var result = new ValueReader<T>(source);
result.ReadValue(out value);
return result;
}
}
class ValueReader<T>
{
IEnumerator<T> _enumerator;
public ValueReader(IEnumerable<T> source)
{
if (source == null) source = new T[0];
_enumerator = source.GetEnumerator();
}
public ValueReader<T> ReadValue(out T value)
{
bool hasNext = _enumerator.MoveNext();
value = hasNext ? _enumerator.Current : default(T);
return this;
}
}
static class TestApp
{
public static void Main()
{
var remainders = new string[] { "test1", "test2", "test3" };
string inputFileName, outputFileName, configFileName, willBeSetToNull;
remainders
.ReadValue(out inputFileName)
.ReadValue(out outputFileName)
.ReadValue(out configFileName)
.ReadValue(out willBeSetToNull);
}
}
答案 6 :(得分:2)
只需在数组中使用索引,例如:
remainers[0]; //same as p1
remainers[1]; //same as p2
remainers[2]; //same as p3
答案 7 :(得分:1)
根据您的描述,我猜测您的用例类似于:
public void SomeMethod( ... )
{
string p1;
string p2;
....
ParseRemainders(string[] remainders, out string p1, out string p2);
...
}
public void SomeOtherMethod( ... )
{
string p1;
string p2;
string p3;
....
ParseRemainders(string[] remainders, out string p1, out string p2, out string p3);
...
}
您不需要以这种方式返回字符串。正如其他答案/评论中已经指出的那样,您只需返回一个字符串数组:
string[] ParseRemainders(string[] remainders)
{
var result = new string[remainder.Length];
result[0] = //whatever p1 would be
result[1] = //whatever p2 would be
//etc.
}
你会像这样使用它:
public void SomeMethod( ... )
{
....
var parsed = ParseRemainders(string[] remainders);
string p1 = parsed[0];
string p2 = parsed[1];
....
}
看起来好多了。
答案 8 :(得分:1)
感觉就像是在尝试过度复杂化一个简单的空检查,只需回到基础并保持简单:
public string GetRemainder(string[] remainders, int index)
{
if ((remainders != null) && (remainders.Length > index))
return remainders[index];
return null;
}
用法:
var inputFileName = GetRemainder(remainder, 0);
var outputFileName = GetRemainder(remainder, 1);
var configFileName = GetRemainder(remainder, 2);