Как построить пустую DeviceInformationCollection? - PullRequest
0 голосов
/ 27 июня 2019

Я реализую интерфейс, который возвращает DeviceInformationCollection. Реализация может истечь (или потерпеть неудачу), и в этом случае я бы хотел вернуть пустую коллекцию. Это позволяет клиентам интерфейса всегда перебирать возвращаемую коллекцию, независимо от того, успешно она выполнена или нет, например,

auto&& devices{ co_await MyType::GetDevicesAsync() };
for (auto&& device : devices)
{
    // Do crazy stuff with 'device'
}

Однако я не могу понять, как построить пустое DeviceInformationCollection. Следующий код «работает», но вызывает неопределенное поведение, когда клиенты используют приведенный выше код:

IAsyncOperation<DeviceInformationCollection> MyType::GetDevicesAsync()
{
    // Doing Guru Meditation
    // ...
    co_return { nullptr };
}

Мой текущий обходной путь - вместо этого вернуть IVector<DeviceInformation> и скопировать элементы внутреннего DeviceInformationCollection в вектор при успехе. Это утомительно и неэффективно. Я бы предпочел просто вернуть DeviceInformationCollection как есть и создать пустую коллекцию при неудаче.

Есть ли способ сделать это?

1 Ответ

2 голосов
/ 01 июля 2019

Официально это не поддерживается, поскольку класс DeviceInformationCollection не предоставляет способ создания пустого экземпляра самого себя.Если вы не можете найти какую-либо функцию в API Windows.Devices.Enumeration, которая делает это для вас, вам не повезло.

Неофициально мы можем заметить, что интерфейсом по умолчанию для класса DeviceInformationCollection является IVectorView.Это означает, что этот интерфейс представляет класс в ABI.Таким образом, вы можете поиграть с этими знаниями, но в целом это очень опасно, потому что API, которые принимают DeviceInformationCollection в качестве входных данных, могут предполагать, что его реализация является эксклюзивной, и, таким образом, полагаться на некоторую внутреннюю разметку, о которой вы можете не знать.Лучше каждый раз возвращать IVectorView полиморфным и безопасным способом.Как то так:

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Devices::Enumeration;

IAsyncOperation<IVectorView<DeviceInformation>> Async()
{
    DeviceInformationCollection devices = co_await // ... some async call

    if (devices)
    {
        co_return devices;
    }

    // Returns empty IVectorView...
    co_return single_threaded_observable_vector<DeviceInformation>().GetView();
}

int main()
{
    for (auto&& device : Async().get())
    {
        printf("%ls\n", device.Name().c_str());
    }
}

...