В то время как обещания и обратные вызовы хорошо работают во многих ситуациях, боль в задней части выражает нечто вроде:
if (!name) {
name = async1();
}
async2(name);
. В итоге вы пройдете через async1
; проверьте, не определено ли name
или нет, и соответственно вызовите обратный вызов.
async1(name, callback) {
if (name)
callback(name)
else {
doSomething(callback)
}
}
async1(name, async2)
Хотя в в порядке хорошо , это раздражает, когда у вас много подобных случаев и обработка ошибок. Вы можете проверить проект здесь . Fibers
помогает в решении проблемы. var Fiber = require('fibers')
function async1(container) {
var current = Fiber.current
var result
doSomething(function(name) {
result = name
fiber.run()
})
Fiber.yield()
return result
}
Fiber(function() {
var name
if (!name) {
name = async1()
}
async2(name)
// Make any number of async calls from here
}
Способ выполнения этой работы состоит в том, чтобы перебирать список и отбрасывать элементы. Это можно сделать с помощью ConvertAll:
List<A> listOfA = new List<C>().ConvertAll(x => (A)x);
Вы также можете использовать Linq:
List<A> listOfA = new List<C>().Cast<A>().ToList();
Процитировать великое объяснение Эрика
Что происходит? Вы хотите, чтобы список жирафов содержал тигра? Вы хотите сбой? или вы хотите, чтобы компилятор защитил вас от сбоя, сделав это незаконным в первую очередь? Мы выбираем последний.
blockquote>Но что, если вы хотите выбрать для сбоя во время выполнения вместо ошибки компиляции? Обычно вы используете Cast & lt;> или ConvertAll & lt;>, но тогда у вас будет две проблемы: она создаст копию списка. Если вы добавите или удалите что-то в новом списке, это не будет отражено в исходном списке. Во-вторых, существует большая производительность и ограничение памяти, так как он создает новый список с существующими объектами.
У меня была такая же проблема, и поэтому я создал класс-оболочку, который может использовать общий список без создания полностью новый список.
В исходном вопросе вы могли бы использовать:
class Test { static void Main(string[] args) { A a = new C(); // OK IList<A> listOfA = new List<C>().CastList<C,A>(); // now ok! } }
и здесь класс-оболочка (+ метод расширения CastList для удобства использования)
public class CastedList<TTo, TFrom> : IList<TTo> { public IList<TFrom> BaseList; public CastedList(IList<TFrom> baseList) { BaseList = baseList; } // IEnumerable IEnumerator IEnumerable.GetEnumerator() { return BaseList.GetEnumerator(); } // IEnumerable<> public IEnumerator<TTo> GetEnumerator() { return new CastedEnumerator<TTo, TFrom>(BaseList.GetEnumerator()); } // ICollection public int Count { get { return BaseList.Count; } } public bool IsReadOnly { get { return BaseList.IsReadOnly; } } public void Add(TTo item) { BaseList.Add((TFrom)(object)item); } public void Clear() { BaseList.Clear(); } public bool Contains(TTo item) { return BaseList.Contains((TFrom)(object)item); } public void CopyTo(TTo[] array, int arrayIndex) { BaseList.CopyTo((TFrom[])(object)array, arrayIndex); } public bool Remove(TTo item) { return BaseList.Remove((TFrom)(object)item); } // IList public TTo this[int index] { get { return (TTo)(object)BaseList[index]; } set { BaseList[index] = (TFrom)(object)value; } } public int IndexOf(TTo item) { return BaseList.IndexOf((TFrom)(object)item); } public void Insert(int index, TTo item) { BaseList.Insert(index, (TFrom)(object)item); } public void RemoveAt(int index) { BaseList.RemoveAt(index); } } public class CastedEnumerator<TTo, TFrom> : IEnumerator<TTo> { public IEnumerator<TFrom> BaseEnumerator; public CastedEnumerator(IEnumerator<TFrom> baseEnumerator) { BaseEnumerator = baseEnumerator; } // IDisposable public void Dispose() { BaseEnumerator.Dispose(); } // IEnumerator object IEnumerator.Current { get { return BaseEnumerator.Current; } } public bool MoveNext() { return BaseEnumerator.MoveNext(); } public void Reset() { BaseEnumerator.Reset(); } // IEnumerator<> public TTo Current { get { return (TTo)(object)BaseEnumerator.Current; } } } public static class ListExtensions { public static IList<TTo> CastList<TFrom, TTo>(this IList<TFrom> list) { return new CastedList<TTo, TFrom>(list); } }
Насколько он не работает, может быть полезно понять ковариацию и контравариантность .
Просто чтобы показать, почему этот не должен , это изменение кода, который вы указали:
void DoesThisWork()
{
List<C> DerivedList = new List<C>();
List<A> BaseList = DerivedList;
BaseList.Add(new B());
C FirstItem = DerivedList.First();
}
Должно ли это работать? Первый элемент в списке относится к типу «B», но тип элемента DerivedList - C.
Теперь предположим, что мы действительно просто хотим создать общую функцию, которая работает в списке некоторых тип, который реализует A, но нам все равно, какой тип:
void ThisWorks<T>(List<T> GenericList) where T:A
{
}
void Test()
{
ThisWorks(new List<B>());
ThisWorks(new List<C>());
}
Прежде всего, прекратите использовать непонятные для понимания имена классов, такие как A, B, C. Используйте животных, млекопитающих, жирафов или продуктов питания, фруктов, апельсинов или что-то там, где отношения понятны.
Тогда возникает вопрос: «Почему я не могу назначить список жирафов переменной типа животного, так как я могу назначить жирафа переменной типа животного?»
Ответ: предположим ты мог. Что может тогда пойти не так?
Ну, вы можете добавить Тигра в список животных. Предположим, мы разрешаем вам помещать список жирафов в переменную, содержащую список животных. Затем вы пытаетесь добавить тигра в этот список. Что происходит? Вы хотите, чтобы список жирафов содержал тигра? Вы хотите сбой? или вы хотите, чтобы компилятор защитил вас от сбоя, сделав это недопустимым в первую очередь?
Мы выбираем последний.
Этот вид преобразования называется «ковариантным «преобразование. В C # 4 мы разрешим вам делать ковариантные преобразования на интерфейсах и делегатах , когда известно, что преобразование всегда безопасно . Подробности см. В статьях моего блога о ковариации и контравариантности. (В понедельник и в четверг на этой неделе появится новая тема по этому вопросу.)
Поскольку C # не разрешает этот тип преобразования наследования в данный момент .
Если вы используете IEnumerable
вместо этого, он будет работать (по крайней мере, в C # 4.0, я не пробовал предыдущие версии). Это просто бросок, конечно же, он все равно будет списком.
Вместо -
List<A> listOfA = new List<C>(); // compiler Error
В исходном коде вопроса , use -
IEnumerable<A> listOfA = new List<C>(); // compiler error - no more! :)
Мне лично нравится создавать библиотеки с расширениями для классов
public static List<TTo> Cast<TFrom, TTo>(List<TFrom> fromlist)
where TFrom : class
where TTo : class
{
return fromlist.ConvertAll(x => x as TTo);
}
Вы можете использовать только списки чтения. Например:
IEnumerable<A> enumOfA = new List<C>();//This works
IReadOnlyCollection<A> ro_colOfA = new List<C>();//This works
IReadOnlyList<A> ro_listOfA = new List<C>();//This works
И вы не можете сделать это для списков, поддерживающих элементы сохранения. Причина в том, что:
List<string> listString=new List<string>();
List<object> listObject=(List<object>)listString;//Assume that this is possible
listObject.Add(new object());
Что теперь? Помните, что listObject и listString - это тот же самый список, поэтому listString теперь имеет элемент объекта - он не должен быть возможным, и это не так.