Да, \
в строковых литералах Python обозначает начало escape-последовательности. На вашем пути у вас есть действительная двухсимвольная escape-последовательность \a
, которая сжимается на символ one , который является ASCII Bell :
>>> '\a'
'\x07'
>>> len('\a')
1
>>> 'C:\meshes\as'
'C:\\meshes\x07s'
>>> print('C:\meshes\as')
C:\meshess
Другие общие escape-последовательности включают в себя \t
(вкладка), \n
(строка), \r
(возврат каретки):
>>> list('C:\test')
['C', ':', '\t', 'e', 's', 't']
>>> list('C:\nest')
['C', ':', '\n', 'e', 's', 't']
>>> list('C:\rest')
['C', ':', '\r', 'e', 's', 't']
Как вы можете видеть, во всех этих примерах обратная косая черта и следующий символ в литерале были сгруппированы вместе, чтобы сформировать единственный символ в финальной строке. Полный список управляющих последовательностей Python здесь здесь .
Существует множество способов борьбы с этим:
r
или R
: >>> r'C:\meshes\as'
'C:\\meshes\\as'
>>> print(r'C:\meshes\as')
C:\meshes\as
os.path.join
... >>> import os
>>> os.path.join('C:', os.sep, 'meshes', 'as')
'C:\\meshes\\as'
pathlib
модуль >>> from pathlib import Path
>>> Path('C:', '/', 'meshes', 'as')
WindowsPath('C:/meshes/as')
Вот код ответа @ dcastro, измененный для C # 7.0 с именованными кортежами и декодированием кортежей, который упрощает обозначение:
public async void Method1()
{
// Version 1, named tuples:
// just to show how it works
/*
var tuple = await GetDataTaskAsync();
int op = tuple.paramOp;
int result = tuple.paramResult;
*/
// Version 2, tuple deconstruction:
// much shorter, most elegant
(int op, int result) = await GetDataTaskAsync();
}
public async Task<(int paramOp, int paramResult)> GetDataTaskAsync()
{
//...
return (1, 2);
}
Подробнее о новых именованных кортежах, литералах и фрагментах кортежей см. : https://blogs.msdn.microsoft.com/dotnet/2017/03/09/new-features-in-c-7-0/
Хорошей особенностью параметров out
является то, что они могут использоваться для возврата данных, даже если функция генерирует исключение. Я думаю, что самым близким эквивалентом этого метода с помощью метода async
будет использовать новый объект для хранения данных, которые могут ссылаться как на метод async
, так и на вызывающий. Другим способом было бы передать делегат, как предложено в другом ответе .
Обратите внимание, что ни один из этих методов не будет иметь какого-либо принудительного исполнения от компилятора, который out
имеет , Т.е. компилятор не потребует установки значения для общего объекта или вызова переданного в делегате.
Вот пример реализации с использованием общего объекта для имитации ref
и out
для использования с методами async
и другими различными сценариями, где ref
и out
недоступны:
class Ref<T>
{
// Field rather than a property to support passing to functions
// accepting `ref T` or `out T`.
public T Value;
}
async Task OperationExampleAsync(Ref<int> successfulLoopsRef)
{
var things = new[] { 0, 1, 2, };
var i = 0;
while (true)
{
// Fourth iteration will throw an exception, but we will still have
// communicated data back to the caller via successfulLoopsRef.
things[i] += i;
successfulLoopsRef.Value++;
i++;
}
}
async Task UsageExample()
{
var successCounterRef = new Ref<int>();
// Note that it does not make sense to access successCounterRef
// until OperationExampleAsync completes (either fails or succeeds)
// because there’s no synchronization. Here, I think of passing
// the variable as “temporarily giving ownership” of the referenced
// object to OperationExampleAsync. Deciding on conventions is up to
// you and belongs in documentation ^^.
try
{
await OperationExampleAsync(successCounterRef);
}
finally
{
Console.WriteLine($"Had {successCounterRef.Value} successful loops.");
}
}
C # 7 + Solution заключается в использовании неявного синтаксиса кортежа.
private async Task<(bool IsSuccess, IActionResult Result)> TryLogin(OpenIdConnectRequest request)
{
return (true, BadRequest(new OpenIdErrorResponse
{
Error = OpenIdConnectConstants.Errors.AccessDenied,
ErrorDescription = "Access token provided is not valid."
}));
}
Результат возврата использует имена свойств, определенные сигнатурой метода. например:
var foo = await TryLogin(request);
if (foo.IsSuccess)
return foo.Result;
Я думаю, что использование ValueTuples, как это, может работать. Сначала нужно добавить пакет ValueTuple NuGet, хотя:
public async void Method1()
{
(int op, int result) tuple = await GetDataTaskAsync();
int op = tuple.op;
int result = tuple.result;
}
public async Task<(int op, int result)> GetDataTaskAsync()
{
int x = 5;
int y = 10;
return (op: x, result: y):
}
Алекс сделал замечательную точку зрения на удобочитаемость. Эквивалентно, функция также является интерфейсом, достаточным для определения возвращаемого типа (типов), и вы также получаете значимые имена переменных.
delegate void OpDelegate(int op);
Task<bool> GetDataTaskAsync(OpDelegate callback)
{
bool canGetData = true;
if (canGetData) callback(5);
return Task.FromResult(canGetData);
}
Абоненты предоставляют лямбду (или названную функцию), а intellisense помогает при копировании имя переменной из делегата.
int myOp;
bool result = await GetDataTaskAsync(op => myOp = op);
Этот конкретный подход похож на метод «Try», где myOp
установлен, если результатом метода является true
. В противном случае вам все равно myOp
.
Вы не можете иметь параметры ref
или out
в методах async
(как уже было отмечено).
Это кричит для некоторого моделирования в движущихся данных:
public class Data
{
public int Op {get; set;}
public int Result {get; set;}
}
public async void Method1()
{
Data data = await GetDataTaskAsync();
// use data.Op and data.Result from here on
}
public async Task<Data> GetDataTaskAsync()
{
var returnValue = new Data();
// Fill up returnValue
return returnValue;
}
Вы получаете возможность повторно использовать свой код более легко, а также более читабельны, чем переменные или кортежи.