Ничто, которое может сделать автор, не может выбрать для открытия в новой вкладке вместо нового окна.
CSS3 предложил target-new , но спецификация была оставлена .
Обратное неверно; указав размеры для окна в третьем аргументе window.open
, вы можете запустить новое окно, когда предпочтение будет указано для вкладок.
К сожалению, тип возврата должен совпадать. То, что вы ищете, называется «ковариация возвращаемого типа», а C # не поддерживает это.
http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=90909
Эрик Липперт, старший разработчик на компиляторе C # команда, упоминает в своем блоге, что они не планируют поддерживать ковариацию возвращаемого типа.
«Такая дисперсия называется« ковариацией возвращаемого типа ». Как я упоминал ранее в этой серии, (а) эта серия не относится к такой дисперсии, и (б) мы не планируем реализовывать такую дисперсию в C #.
blockquote>Стоит прочитать Статьи Эрика о ковариации и контравариантности.
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
Я столкнулся с подобными ситуациями и хочу дать более конкретный пример того, почему это недопустимо. Общие части моего приложения связаны с интерфейсом, который содержит свойства, и предоставляет данные через этот интерфейс в другую часть приложения, содержащую конкретные классы, реализующие эти интерфейсы.
Цель, я думаю, похожа на вашу: классы-классы, чтобы иметь больше функциональности, а интерфейс обеспечивает абсолютный минимум, необходимый для общей части приложения. Для интерфейса очень полезно выявить наименьшее количество функциональных возможностей, поскольку оно максимизирует совместимость / повторное использование. То есть он не требует, чтобы исполнитель интерфейса реализовал больше, чем это необходимо.
Однако учтите, был ли у вас наборщик по этому свойству. Вы создаете экземпляр вашего конкретного класса и передаете его некоторому универсальному помощнику, который принимает ISomeData. В коде этого помощника они работают с ISomeData. Без какого-либо типа T, где T: new () или заводской шаблон, они не могут создавать новые экземпляры для ввода данных, которые соответствуют вашей конкретной реализации. Они просто возвращают список, который реализует IEnumerable:
instanceISomeData.Data = new SomeOtherTypeImplementingIEnumerable ();
Если SomeOtherTypeImplementingIEnumerable не наследует List, но вы реализовали .Data как список , то это недопустимое присвоение. Если компилятор разрешил вам это сделать, тогда сценарии, подобные этому, будут повреждены во время выполнения, потому что SomeOtherTypeImplementingIEnumerable не может быть передан в List. Однако в контексте этого помощника, работающего с ISomeData, он все равно не нарушал интерфейс ISomeData, и назначение любого типа, поддерживающего IEnumerable, должно быть действительным. Таким образом, ваша реализация, если бы компилятор допустил это, мог нарушить превосходный код, работающий с интерфейсом.
Вот почему вы не можете реализовать .Data как производный тип. Вы более жестко ограничиваете реализацию .Data только за исключением List, а не любого IEnumerable. Таким образом, хотя интерфейс говорит, что «любой IEnumerable разрешен», ваша конкретная реализация заставит ранее существовавший код, поддерживающий ISomeData, внезапно сломаться при работе с вашей реализацией интерфейса.
Конечно, вы действительно не запускаете в этот сценарий только с помощью геттера, но это усложнит ситуацию, чтобы разрешить его в сценариях получения, но не иначе.
Обычно я использую решение Джейка или решение Алиото, в зависимости от того, насколько я себя чувствую в настоящий момент .
Интерфейсы требуют, чтобы подпись метода соответствовала сигнатуре контракта точно .
Вот более простой пример, который также не будет компилироваться:
interface IFoo
{
Object GetFoo();
}
class Foo : IFoo
{
public String GetFoo() { return ""; }
}
Теперь о том, что с этим делать, я бы позволил интерфейсу диктовать реализацию. Если вы хотите, чтобы контракт был IEnumerable<T>
, то это то, что должно быть в классе. Интерфейс является самым важным здесь, так как реализация свободна настолько гибкой, насколько это необходимо.
Просто убедитесь, что IEnumerable<T>
- лучший выбор здесь. (Это очень субъективно, поскольку я мало знаю о вашем домене или цели этих типов в вашем приложении. Удачи!) [/ G4]
Подпись члена не может быть разной.
Вы все равно можете вернуть List<string>
в рамках метода get
, но подпись должна быть такой же, как интерфейс.
. Просто измените:
public List<string> Data { get { return m_MyData; } }
на
public IEnumerable<string> Data { get { return m_MyData; } }
Что касается вашего другого варианта: изменение интерфейса для возврата List
. Этого следует избегать. Это плохая инкапсуляция и считается запахом кода кода .
Вы можете реализовать следующее:
public class MyData : ISomeData
{
private List<string> m_MyData = new List<string>();
public IEnumerable<string> Data { get { return m_MyData; } }
}
Хм, это недостаток, я бы сказал, нет.
В любом случае, я бы работал вокруг него, как ответ дарина, или, если вы явно хотите использовать аксессуар List, это выглядит так:
public class MyData : ISomeData
{
IEnumerable<string> ISomeData.Data
{
get
{
return _myData;
}
}
public List<string> Data
{
get
{
return (List<string>)((ISomeData)this).Data;
}
}
}
Если вам нужен список в вашем интерфейсе (известное количество элементов со случайным доступом), вам следует рассмотреть возможность изменения интерфейса на
public interface ISomeData
{
ICollection<string> Data { get; }
}
. Это даст вам как расширяемость, так и которые вам нужны из списка.
List<T>
не может быть легко подклассифицирован, что означает, что у вас может возникнуть проблема с возвратом этого точного типа из всех классов, которые хотят реализовать ваш интерфейс.
ICollection<T>
, с другой стороны, могут быть реализованы различными способами.
Я бы выбрал вариант 2:
. Цель определения интерфейса в вашем коде - определить контракт, и поэтому вы и другие люди, которые реализуют ваш интерфейс, знают, что нужно согласовать. Независимо от того, задаете ли вы IEnumerable или List в своем интерфейсе, действительно проблема контракта и относится к руководству по разработке структуры. Вот вам целая книга , чтобы обсудить это.
Лично я бы разоблачил IEnumerable и внедрил MyData в IEnumerable, и вы можете вернуть его обратно в метод List в RandomAccess ().
Что, если вы изменили свой интерфейс, чтобы расширить IEnumerable, чтобы вы могли перечислять объект и редактировать данные через свойство класса.
public interface ISomeData : IEnumerable<string>
{
IEnumerable<string> Data { get; }
}
public class MyData : ISomeData
{
private List<string> m_MyData = new List<string>();
public List<string> Data { get { return m_MyData; }
public IEnumerator<string> GetEnumerator()
{
return Data;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Почему бы просто не вернуть список из вашего интерфейса ...
public interface ISomeData
{
List<string> Data { get; }
}
Если вы знаете, что ваши потребители собираются как перебирать его (IEnumerable), так и добавлять к нему (IList), тогда кажется логически просто вернуть список.
Что делать, если вы получили доступ к вашему объекту MyData через интерфейс ISomeData? В этом случае IEnumerable может быть базового типа, не назначаемого List.
IEnumerable<string> iss = null;
List<string> ss = iss; //compiler error
EDIT:
Я понимаю, что вы имеете в виду из своих комментариев.
Во всяком случае, я бы сделал в вашем случае:
public interface ISomeData<T> where T: IEnumerable<string>
{
T Data { get; }
}
public class MyData : ISomeData<List<string>>
{
private List<string> m_MyData = new List<string>();
public List<string> Data { get { return m_MyData; } }
}
Преобразование в общий интерфейс с соответствующими предложениями ограничений я считаю лучшим как гибкостью, так и удобочитаемостью.
Для того, что вы хотите сделать, вы, вероятно, захотите реализовать интерфейс явно с членом класса (не интерфейса), который возвращает List вместо IEnumerable ...
public class MyData : ISomeData
{
private List<string> m_MyData = new List<string>();
public List<string> Data
{
get
{
return m_MyData;
}
}
#region ISomeData Members
IEnumerable<string> ISomeData.Data
{
get
{
return Data.AsEnumerable<string>();
}
}
#endregion
}
Изменить: Для пояснения это позволяет классу MyData возвращать List, когда он рассматривается как экземпляр MyData; в то же время позволяя ему возвращать экземпляр IEnumerable при обращении как экземпляр ISomeData.