Я думаю, что следующий вспомогательный метод также может решить проблему.
private TResult InvokeAsyncFuncSynchronously<TResult>(Func< Task<TResult>> func)
{
TResult result = default(TResult);
var autoResetEvent = new AutoResetEvent(false);
Task.Run(async () =>
{
try
{
result = await func();
}
catch (Exception exc)
{
mErrorLogger.LogError(exc.ToString());
}
finally
{
autoResetEvent.Set();
}
});
autoResetEvent.WaitOne();
return result;
}
Может использоваться следующим образом:
InvokeAsyncFuncSynchronously(Service.GetCustomersAsync);
Причина состоит в том, что при инстанцировании шаблона класса, все его объявления (не определения) его функций членства инстанцируют также. Шаблон класса инстанцируют точно, когда полное определение специализации требуется. Это имеет место, когда это используется в качестве базового класса, например, в качестве в Вашем случае.
Поэтому то, что происходит, - то, что A<B>
инстанцирован в [1 113]
class B : public A<B>
, в котором точка B
еще не является полным типом (это после закрывающей фигурной скобки определения класса). Однако A<B>::action
объявление требует B
быть завершенным, потому что это сканирования в пределах него:
Subclass::mytype
то, Что необходимо сделать, задерживает инстанцирование к некоторой точке, в которой B
завершено. Один способ сделать это состоит в том, чтобы изменить объявление action
для создания этого членским шаблоном.
template<typename T>
void action(T var) {
(static_cast<Subclass*>(this))->do_action(var);
}
Это все еще безопасно с точки зрения типов, потому что, если var
не имеет правильного типа, передавая var
к [1 111], перестанет работать.
Можно обойти это при помощи класса черт:
Это требует, чтобы Вы настроили specialsed класс черт для каждого фактического класса, который Вы используете.
template<typename SubClass>
class SubClass_traits
{};
template<typename Subclass>
class A {
public:
void action(typename SubClass_traits<Subclass>::mytype var)
{
(static_cast<Subclass*>(this))->do_action(var);
}
};
// Definitions for B
class B; // Forward declare
template<> // Define traits for B. So other classes can use it.
class SubClass_traits<B>
{
public:
typedef int mytype;
};
// Define B
class B : public A<B>
{
// Define mytype in terms of the traits type.
typedef SubClass_traits<B>::mytype mytype;
public:
B() {}
void do_action(mytype var) {
// Do stuff
}
};
int main(int argc, char** argv)
{
B myInstance;
return 0;
}
Не точно, что Вы спрашивали, но можно сделать действие шаблонной функцией членства:
template<typename Subclass>
class A {
public:
//Why doesn't it like this?
template<class V> void action(V var) {
(static_cast<Subclass*>(this))->do_action();
}
};
class B : public A<B> {
public:
typedef int mytype;
B() {}
void do_action(mytype var) {
// Do stuff
}
};
int main(int argc, char** argv) {
B myInstance;
return 0;
}
Необходимо использовать указатель или ссылку, поскольку надлежащий тип не известен в это время, компилятор не может инстанцировать его.
Вместо этого попытка:
void action(const typename Subclass::mytype &var) {
(static_cast<Subclass*>(this))->do_action();
}
Вы происходите B
от A<B>
, таким образом, первая вещь, которую делает компилятор, как только это видит определение класса B
, состоит в том, чтобы попытаться инстанцировать A<B>
. Чтобы сделать это, этому нужно к известному B::mytype
для параметра action
. Но так как компилятор находится только в процессе выяснения фактического определения B
, это еще не знает этот тип, и Вы получаете ошибку.
Один путь вокруг этого, должен был бы объявить тип параметра как другой шаблонный параметр, вместо внутренней части производный класс:
template<typename Subclass, typename Param>
class A {
public:
void action(Param var) {
(static_cast<Subclass*>(this))->do_action(var);
}
};
class B : public A<B, int> { ... };