Как указывает ответ Николаса Кэри, Console.WriteLine просто записывает в поток текста TextWriter Console.Out.
Зная эту информацию, мы можем переписать оригинальный метод следующим образом:
public static void GetInstalledApps(TextWriter writer)
{
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key);
{
foreach (string subkey_name in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(subkey_name);
{
// While this won't throw an Exception when GetValue returns null,
// as WriteLine will "output nothing" for null values,
// this logic might be bugged: the next example has a possible solution.
// No more hard-coding of Console.WriteLine, instead we use
// writer.WriteLine, where writer is the TextWriter that
// has been supplied as an argument.
writer.WriteLine(subkey.GetValue("DisplayName"));
}
}
}
}
Тогда код можно следующим образом, чтобы получить оригинальную семантику записи на консоль.
GetInstalledApps(Console.Out);
Теперь предположим, что мы хотим, чтобы написать в различного потока, мы должны просто указать его. Из link provided in a comment мы видим, как легко создать StreamWriter для файла. StreamWriter extends TextWriter может, таким образом, быть передан нашему методу. Таким образом, легко записать в файл:
using (StreamWriter writer = new StreamWriter("theFile.txt"))
{
GetInstalledApps(writer);
}
Однако я бы предложил переместить запись из метода. То есть, метод должен делать только один вещь: он должен только выбрать установленных приложений.
Таким образом, рассмотрим следующий код, который использует yield
для создания неявного итератора. Тем не менее, создание/возвращение списка программно или использование LINQ для генерации результата Enumerable также отлично работали бы. Я также модифицировал функцию таким образом, чтобы она возвращала значение, даже если нет DisplayName.
public static IEnumerable<string> GetInstalledApps()
{
string registry_key = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key = Registry.LocalMachine.OpenSubKey(registry_key);
{
foreach (string subkey_name in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(subkey_name);
{
// I've also added fix for what I believe that was a "silent bug"
// that allowed entries without a display name to be "lost".
// This code returns the subkey name if there is no display name.
var value = subkey.GetValue("DisplayName") as string;
if (!string.IsNullOrEmpty(value)) {
yield return value;
} else {
// No display name, but there is still an entry!!
yield return subkey_name;
}
}
}
}
}
Тогда мы можем двигаться использования данных вне функцию, которая извлекает данные - это уменьшает сцепление и делает код более гибким. Возможно, позже мы захотим использовать код для обновления ListBox вместо записи в файл.
using (StreamWriter writer = new StreamWriter("theFile.txt"))
{
foreach (var app in GetInstalledApps()) {
writer.WriteLine(app);
}
}
Если GetInstalledApps
написано в приведенной выше способом, где она отделяет из концерна IO, то также может быть использован с File.*AllText
после простого преобразования IEnumerable<string>
в соответствующую строку, например, с String.Join
. Тем не менее, я все равно использовал бы цикл и WriteLine в соответствии с приведенным выше, если бы мне не понадобилось возвращать строковый объект.
http://msdn.microsoft.com/en-us/library/6ka1wd3w(v=vs.110).aspx – Habib
http://stackoverflow.com/questions/4501511/c-sharp-realtime-console- output-redirection – Nick
http://www.dotnetperls.com/streamwriter –