Процитировать великое объяснение Эрика
Что происходит? Вы хотите, чтобы список жирафов содержал тигра? Вы хотите сбой? или вы хотите, чтобы компилятор защитил вас от сбоя, сделав это незаконным в первую очередь? Мы выбираем последний.
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); } }
Благодаря @PaulAsjes мне удалось собрать хорошее решение.
Окончательный вариант выглядит следующим образом: для всех, кому интересно.
const CardDetail = ({ stripe }) => {
/* new state, ready */
const [{ ready, token }, setState] = React.useState({
ready: false,
});
const submit = () => {
stripe.createToken({ name: "Name" }).then(({ token }) => {
setState({ token });
});
};
/* I added this */
const onReady = () => setState({ error, ready: true });
return (
<div>
<label>
<p>Please provide your card details.</p>
{/* this shows/hides my spinner */}
{!ready && <MyLoadingSpinner size="small" />}
{/* and listen here ↓ */}
<CardElement onReady={onReady} style={{ base: { fontSize: "20px" } }} />
</label>
<button onClick={submit}>
Save Card
</button>
</div>
);
};