Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, Jack128, Вы писали:
G>>>>Ты же наследование делаешь чтобы обращаться к наследникам через ссылку на базовый класс. Соответственно непонятно зачем в наследнике менять типы, и как сообщить компилятору какой тип правильный. G>>>>Вообще есть вероятность нарушения LSP при таком поведении. J>>>"такое поведение" — это поведение MyCollection2? естественно нарушит LSP. И именно этого и поможет избежать предлагаемая фича. G>>Как она поможет избежать? Примером кода пожалуйста.
J>class CollectionItem {} J>class Collection J>{ J> protected virtual CollectionItem CreateItem() {...} J>}
J>class MyCollectionItem: CollectionItem {} J>class MyCollection: Collection J>{ J> protected override MyCollectionItem CreateItem() {...} J>}
J>class MyCollection2: MyCollection J>{ J> // protected override CollectionItem CreateItem() {...} так ошибка компиляции, так как CollectionItem не является MyCollectionItem, ковариантность нарушится. Раз не компилируется, то LSP не сможем нарушить. J> protected override MyCollectionItem CreateItem() {...} // а так вполне можно.
J>}
Надо как-то связь межу MyCollection2 и MyCollectionItem передавать. А это делается через генерики.
Делай так:
class CollectionItem {}
class Collection<T>
{
protected virtual T CreateItem() {...}
}
Re[10]: Ковариантность по возвращаемому значению в виртуальных методах.
Здравствуйте, samius, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, samius, Вы писали:
S>>>>>вот это уже ковариантность. G>>>>Да ну? А где здесь функтор?
S>>>А зачем функтор? G>>А что такое вариантность вообще? S>Это смотря где. S>А в контексте программирования под ковариантностью и контрвариантностью подразумевают взаимозаеняемость и эквивалентность типов в определенных ситуациях на основе отношений типов (частное/общее).
С точки зрения теории категорий (на которой основаны системы типов). Есть объект (тип) и стрелки между ними (отношения тип-подтип, она же взаимозаменяемость) вместе это категория. Есть функтор (генерик или массив), который преобразует одну категорию в другую (на самом деле в подкатегорию исходной, но это тут неважно). Так вот в зависимости он воздействия функтора на стрелки его вариантность и определяется. В принципе могут быть и другие функторы, но, насколько я знаю, в C# других, похожих на функторы, частей нету.
Здравствуйте, Аноним, Вы писали:
А>Я исхожу из реальной проблемы: часто встречаешь набор функций, которые относятся только к enum'у, но хостятся в классе Utils, Converters и т.п. Если их можно было бы засунуть в сам enum, было бы понятно:
Решение не менее реальной проблемы на Java:
// V - для своего lookupIndexedEnumValue() в ответ на жирный аналог в LengthUnit.Parsepublic enum RoleType implements IIndexable<String>, IRoleAuthorizationContextFactory {
ADMINISTRATOR("admin") {
@Override
public IRoleAuthorizationContext createRoleAuthorizationContext(Request request) {
return new AdministratorRoleAuthorizationContext();
}
},
EDITOR("editor") {
@Override
public IRoleAuthorizationContext createRoleAuthorizationContext(Request request) {
return new EditorRoleAuthorizationContext(request);
}
};
private final String alias;
RoleType(String alias) {
this.alias = alias;
}
@Override
public final String getIndexKey() {
return alias;
}
}
Друга ищи не того, кто любезен с тобой, кто с тобой соглашается, а крепкого советника, кто полезного для тебя ищет и противится твоим необдуманным словам.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, samius, Вы писали:
S>>А в контексте программирования под ковариантностью и контрвариантностью подразумевают взаимозаеняемость и эквивалентность типов в определенных ситуациях на основе отношений типов (частное/общее).
G>С точки зрения теории категорий (на которой основаны системы типов). Есть объект (тип) и стрелки между ними (отношения тип-подтип, она же взаимозаменяемость) вместе это категория.
Стрелка тип-подтип уже не дает взаимозаменяемости в полной мере, т.к. в зависимости от обстоятельств, либо подтип может быть сконвертирован в тип, либо тип в подтип.
G>Есть функтор (генерик или массив), который преобразует одну категорию в другую (на самом деле в подкатегорию исходной, но это тут неважно). Так вот в зависимости он воздействия функтора на стрелки его вариантность и определяется. В принципе могут быть и другие функторы, но, насколько я знаю, в C# других, похожих на функторы, частей нету.
Генерики там совсем с другой стороны. Функтор (согласно википедии же) определяет функцию преобразования типа F : C x C -> C и совершенно не имеет отношения к массиву. Ну разве что косвенно, т.к. преобразования типов можно делать как на уровне string -> object, так и на уровне T<X> -> T<Y> или string[] -> object[].
C# изначально позволял работать с вариантностью на уровне параметров методов, возврата результата метода, исключений и т.п. И лишь недавно стал более-менее полно поддерживать вариантность дженериков.
Re[9]: Ковариантность по возвращаемому значению в виртуальных методах.
Здравствуйте, gandjustas, Вы писали:
G>Надо как-то связь межу MyCollection2 и MyCollectionItem передавать. А это делается через генерики. G>Делай так:
G>
Здравствуйте, samius, Вы писали:
S>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, samius, Вы писали:
S>>>А в контексте программирования под ковариантностью и контрвариантностью подразумевают взаимозаеняемость и эквивалентность типов в определенных ситуациях на основе отношений типов (частное/общее).
G>>С точки зрения теории категорий (на которой основаны системы типов). Есть объект (тип) и стрелки между ними (отношения тип-подтип, она же взаимозаменяемость) вместе это категория. S>Стрелка тип-подтип уже не дает взаимозаменяемости в полной мере, т.к. в зависимости от обстоятельств, либо подтип может быть сконвертирован в тип, либо тип в подтип.
Ни слова не понял.
G>>Есть функтор (генерик или массив), который преобразует одну категорию в другую (на самом деле в подкатегорию исходной, но это тут неважно). Так вот в зависимости он воздействия функтора на стрелки его вариантность и определяется. В принципе могут быть и другие функторы, но, насколько я знаю, в C# других, похожих на функторы, частей нету.
S>Генерики там совсем с другой стороны. Функтор (согласно википедии же) определяет функцию преобразования типа F : C x C -> C и совершенно не имеет отношения к массиву. Ну разве что косвенно, т.к. преобразования типов можно делать как на уровне string -> object, так и на уровне T<X> -> T<Y> или string[] -> object[]. Функтор эта статья? Там нет определения, которое ты написал.
Давай с начала.
Есть категория (множество) типов T. Есть преобразование этой категории, которое для каждого типа t из T позволяет получить IEnumerable<t> (по сути тоже из T). Это преобразование называется функтором (в данном случае эндофунктором, но это неважно).
Аналогичное можно сказать по Action<T>, и про массивы.
Так вот в категории кроме объектов (типов) есть еще и между объектами. В нашем случае стрелки — отношения "тип-подтип", которые задаются наследованием. Так вот функтор, сохраняющий это отношение в результирующей категории называется ковариантным.
а вот твой пример
object x = 1 as int;
Ну никак он на функторы не похож.
S>C# изначально позволял работать с вариантностью на уровне параметров методов, возврата результата метода, исключений и т.п. И лишь недавно стал более-менее полно поддерживать вариантность дженериков.
На уровне языка — да, на уровне clr всегда так и работало.
Здравствуйте, VladD2, Вы писали:
Ц>>>Есть "покруче". KP>>ААА! Вот откуда эту идею в Rust притащили :)) VD>Гы-гы (2005-й год). Думаю, что и до этого где-то было.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, samius, Вы писали:
G>>>С точки зрения теории категорий (на которой основаны системы типов). Есть объект (тип) и стрелки между ними (отношения тип-подтип, она же взаимозаменяемость) вместе это категория. S>>Стрелка тип-подтип уже не дает взаимозаменяемости в полной мере, т.к. в зависимости от обстоятельств, либо подтип может быть сконвертирован в тип, либо тип в подтип.
G>Ни слова не понял.
Не суть важно. В двух словах отношение тип-подтим и взаимозаменяемость это не одно и то же.
S>>Генерики там совсем с другой стороны. Функтор (согласно википедии же) определяет функцию преобразования типа F : C x C -> C и совершенно не имеет отношения к массиву. Ну разве что косвенно, т.к. преобразования типов можно делать как на уровне string -> object, так и на уровне T<X> -> T<Y> или string[] -> object[]. G>http://ru.wikipedia.org/wiki/%D0%A4%D1%83%D0%BD%D0%BA%D1%82%D0%BE%D1%80_(%D0%BC%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) эта статья? Там нет определения, которое ты написал. Covariance and contravariance (computer science) / 2. Origin of the rerms G>Давай с начала.
Давай
G>Есть категория (множество) типов T.
Что бы построить категорию, нужен еще и морфизм. Одного множества недостаточно для указания категории.
Вариантность рассматривается на категории C, которую образует множество типов и морфизм отношения подтипа.
G>Есть преобразование этой категории, которое для каждого типа t из T позволяет получить IEnumerable<t> (по сути тоже из T). Это преобразование называется функтором (в данном случае эндофунктором, но это неважно).
Я поспорю что это построение T -> IEnumerable<T> есть функтор в общем понимании. В C# он стал функтором только с тех пор, как стал сохранять морфизм отношения подтипов.
G>Аналогичное можно сказать по Action<T>, и про массивы.
Массивы ограниченно сохрнаняли морфизм подтипов с первой версии. А Action<T> — нет.
G>Так вот в категории кроме объектов (типов) есть еще и между объектами. В нашем случае стрелки — отношения "тип-подтип", которые задаются наследованием. Так вот функтор, сохраняющий это отношение в результирующей категории называется ковариантным.
Я только уточню что любой функтор сохраняет структуру морфизма по определению. При этом, ковариантный сохраняет направление отношения, а контрвариантный разворачивает.
G>а вот твой пример
G>
G>object x = 1 as int;
G>
G>Ну никак он на функторы не похож.
Здесь ты прав. Я поспешил и неверно интерпретировал примеры из статьи.
S>>C# изначально позволял работать с вариантностью на уровне параметров методов, возврата результата метода, исключений и т.п. И лишь недавно стал более-менее полно поддерживать вариантность дженериков. G>На уровне языка — да, на уровне clr всегда так и работало.
А там я тоже поспешил в отношении вариантности возврата результата метода, чем C# никогда не страдал. Это как раз то, о чем пишет Jack128. Подробнее тут (http://en.wikipedia.org/wiki/Covariant_return_type)
Здравствуйте, Санёк81, Вы писали:
Сё>Здравствуйте, k0st1x, Вы писали:
K>>как временное решение или workaround, K>>можно примотать мизинец к безымянному пальцу, ну или приклееть суперклеем.
Сё>Пиши на VB
прискорбно, что смена языка — единственное решение неудобства
Нельзя ли сделать так, чтобы enum'ы реализовывали интерфейс IEquatable<> ?
Re[6]: Что нужно добавить в C#?
От:
Аноним
Дата:
22.02.13 22:47
Оценка:
Здравствуйте, Цыба, Вы писали:
Ц>Что здесь плохого?
Не уверен, что я все понял правильно, и если нет, сразу простите дурака. Но если да, то это типичный пример ява-стайл ОООП'а (Over-engineered OOP), который меня просто убивает.
Плохо в этом примере то, что он скрывает неявное множественное наследование от System.Attribute и System.Enum.
Вообще, я за ООП в целом и множественное наследование в частности в каждом УЯП, но МН нужно таааааак редко... и это явно не тот случай. Пусть котлеты будут отдельно, а мухи — отдельно:
public enum RoleType
{
ADMINISTRATOR,
EDITOR;
public IRoleAuthorizationContext ToContext(Request request)
{
switch (value)
{
case ADMINISTRATOR: return new AdministratorRoleAuthorizationContext(); break;
case EDITOR: return new EditorRoleAuthorizationContext(request); break;
default: throw new Exception("A new role was added with no converting code."); break;
}
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]public class RoleTypeAttribute : System.Attribute
{
...
}
public interface IService
{
[RoleTypeAttribute(RoleType.ADMINISTRATOR)]
void foo();
[RoleTypeAttribute(RoleType.EDITOR)]
void bar();
}
Обратите внимание на выделенное. Где ему место в вашем примере?
И раз уж речь зашла о нововведениях и Ските, то вот тут:
Well, I can't answer why it's not available, but I can confirm that it's not a CLI issue. The CLI spec doesn't mention it (as far as I can see) and if you use IL directly you can create a generic attribute. The part of the C# 3 spec that bans it — section 10.1.4 "Class base specification" doesn't give any justification.
The annotated ECMA C# 2 spec doesn't give any helpful information either, although it does provide an example of what's not allowed.
My copy of the annotated C# 3 spec should arrive tomorrow... I'll see if that gives any more information. Anyway, it's definitely a language decision rather than a runtime one.
EDIT: Answer from Eric Lippert (paraphrased): no particular reason, except to avoid complexity in both the language and compiler for a use case which doesn't add much value.
Так что, если бы они ЭТО пофиксили, то ваш пример (именно ваш, без привязки к методам и с потенциальной неоднозначностью ролей) мог бы выглядеть так:
public enum RoleType
{
ADMINISTRATOR,
EDITOR;
public IRoleAuthorizationContext ToContext(Request request)
{
switch (value)
{
case ADMINISTRATOR: return new AdministratorRoleAuthorizationContext(); break;
case EDITOR: return new EditorRoleAuthorizationContext(request); break;
default: throw new Exception("A new role was added with no converting code."); break;
}
}
}
public interface IService
{
[System.EnumAttribute<RoleType>(RoleType.ADMINISTRATOR)]
void foo();
[System.EnumAttribute<RoleType>(RoleType.EDITOR)]
void bar();
}
P.S. Чего мне в дотнете всегда не хватало, это набора стандартизированных интерфейсов, чтоб не пилить самому каждый раз, особенно в ремотных системах. System.EnumAttribute<T> это как раз из этой оперы.
Синтаксис Java может показаться непривычным и немножко сбивать с толку.
А>Не уверен, что я все понял правильно, и если нет, сразу простите дурака. Но если да, то это типичный пример ява-стайл ОООП'а (Over-engineered OOP), который меня просто убивает.
Пускай. Дело привычки, простоты повторного использования и издержек самой Java.
А>Плохо в этом примере то, что он скрывает неявное множественное наследование от System.Attribute и System.Enum.
Тут нет множественного наследования и не может такового быть. Перечисления неявно наследуются исключительно от java.lang.Enum<E>, хотя и имплементирует интерфейс. Пожалуй, я неудачно выбрал имена Role и RoleType? То, что вы, возможно, посчитали за наследование от System.Attribute -- аннотация @Role, аналог [RoleAttribute] (укажу ниже). Я здесь имел в виду то, что элемент такого перечисления может появляться в качестве значения аннотации. В .NET такое невозможно, насколько мне известно, и поэтому приходится довольствоваться только тем, что может быть константой (по поводу typeof не знаю, то в Java это считается константой, как и элемент перечисления). Могу ошибаться.
А>Обратите внимание на выделенное. Где ему место в вашем примере?
Я тогда просто хотел показать, что полноценный объект, хоть и с ограничениями enum и @interface на типы, а не простая целочисленная константа, может указываться в аннотации (аттрибута). Собственно, тогда уж и недостающий пример:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Role { // аналог аттрибута; аналога AllowMultiple не существует
RoleType value(); // RoleType - само перечесление, которое можно использовать в качестве значения анотации (value можно опускать)
}
@interface в Java тоже не может быть generic, как и enum. В моём случае перечисление просто обязуется реализовать параметризированный generic-интерфейс (для поиска члена перечисления не по имени, а по алиасу). Мне, честно говоря, сложно подобрать случай, в котором нужны generic-аттрибуты, когда из доступных значений аттрибутов могут быть только примитивы и строки.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Tom, Вы писали:
Tom>>А что не так с x64 если не секрет?
AVK>Он крайне дохлый в плане оптимизации. Поэтому первым будут заменять именно его.
попробовал выполнить код(ниже) на жаве и сишарп
2.9гг
c# — релиз, nf4.5(64 бита за 1700 млс, 32 — 2044)
java — jdk 7u15 выполняет за 2000 млс,
так что вроде с оптимизациями нормально или чтото не так под эклипсом запустил?
public class Tester {
/**
* @param args
*/public static void main(String[] args) {
long start = System.currentTimeMillis();
int m = 1024 * 1024;
Test(100 * m);
long end = System.currentTimeMillis();
System.out.println("time" + (end - start));
}
static void Test(int limit_) {
int limit = limit_;
int sqr_lim;
boolean[] is_prime = new boolean[limit + 1];
int x2, y2;
int i, j;
int n;
sqr_lim = (int) Math.sqrt(limit);
for (i = 0; i <= limit; i++)
is_prime[i] = false;
is_prime[2] = true;
is_prime[3] = true;
x2 = 0;
for (i = 1; i <= sqr_lim; i++) {
x2 += 2 * i - 1;
y2 = 0;
for (j = 1; j <= sqr_lim; j++) {
y2 += 2 * j - 1;
n = 4 * x2 + y2;
if ((n <= limit) && (n % 12 == 1 || n % 12 == 5))
is_prime[n] = !is_prime[n];
// n = 3 * x2 + y2;
n -= x2;
if ((n <= limit) && (n % 12 == 7))
is_prime[n] = !is_prime[n];
// n = 3 * x2 - y2;
n -= 2 * y2; //if ((i > j) && (n <= limit) && (n % 12 == 11))
is_prime[n] = !is_prime[n];
}
}
for (i = 5; i <= sqr_lim; i++) {
if (is_prime[i]) {
n = i * i;
for (j = n; j <= limit; j += n) {
is_prime[j] = false;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace cappTestPrime
{
public class Tester
{
public static void Main(String[] args)
{
var sw = Stopwatch.StartNew();
int m = 1024 * 1024;
Test(100 * m);
var end = sw.ElapsedMilliseconds;
Console.WriteLine("elapsed=" + end);
}
static void Test(int limit_)
{
int limit = limit_;
int sqr_lim;
bool[] is_prime = new bool[limit + 1];
int x2, y2;
int i, j;
int n;
sqr_lim = (int)Math.Sqrt(limit);
for (i = 0; i <= limit; i++)
is_prime[i] = false;
is_prime[2] = true;
is_prime[3] = true;
x2 = 0;
for (i = 1; i <= sqr_lim; i++)
{
x2 += 2 * i - 1;
y2 = 0;
for (j = 1; j <= sqr_lim; j++)
{
y2 += 2 * j - 1;
n = 4 * x2 + y2;
if ((n <= limit) && (n % 12 == 1 || n % 12 == 5))
is_prime[n] = !is_prime[n];
// n = 3 * x2 + y2;
n -= x2;
if ((n <= limit) && (n % 12 == 7))
is_prime[n] = !is_prime[n];
// n = 3 * x2 - y2;
n -= 2 * y2; // if ((i > j) && (n <= limit) && (n % 12 == 11))
is_prime[n] = !is_prime[n];
}
}
for (i = 5; i <= sqr_lim; i++)
{
if (is_prime[i])
{
n = i * i;
for (j = n; j <= limit; j += n)
{
is_prime[j] = false;
}
}
}
}
}
}
Здравствуйте, AndrewVK, Вы писали:
AVK>Основная проблема с ПМ, как озвучил Мэдс, в АТД. Это новый first class citizen, который отчасти дублирует существующие сущности в языке. Так что АТД ака discriminated unions на данный момент однозначно no way. Теоретически можно обойтись без АТД существующими типами с какими то дополнительными доработками, но тут нужно говорить более конкретно.
Перевожу на русский язык — "Нам хочется еще лет 5-10 по-вредничать.", потому как в ином случае объяснить подобные утверждения можно только недостатком знаний и не желанием смотреть на то что валяется под ногами.
Начнем с того, что ПМ не имеет никакого отношения к АлгТД (АлгТД потому как АТД принято расшифровывать как Абстрактный Тип Данных, что из другой оперы).
Две основные фичи ПМ, которые делают его мощной штукой это:
1. Рекурсивность.
2. Динамические проверки типов.
Здесь BaseRule и ExtentionType — это поля имеющие вариантный тип (АлгТД), а все остальное — это классы.
RuleSymbol — это свойство типа RuleDefSymbol, а ExtensibleRuleSymbol, ExtentionRuleSymbol, SimpleRuleSymbol и RegularRuleSymbol его наследники. Причем непрямые.
Мы используем классы для символов, так как нам нужна расширяемость, а вариантные типы этого не обеспечивают.
Кстати, закрытость набора можно обеспечить банальным seled.
Есть только одна фича МП которая упирается в возможности АлгТД — описание паттерна в виде конструктора. Это позволяет несколько сократить синтаксис описывая состояние объекта в виде параметров его конструктора. Чтобы это было возможно нужно иметь соответствие между полями типа и параметрами конструктора.
Так вот, во-первых можно обходиться несколько более длинным синтаксисом в котором внутри паттерна явно указывается имя члена который нужно проверить. Зачастую это даже оказывается более удобным, так как позволяет не перечислять все параметры конструктора забивая большую часть вилдкардами (знаком _ в ФЯ).
Во-вторых, соответствие между аргументами конструктора и полями можно задать и способами отличными от тех что используются в АлгТД. Например, можно обратиться к опыту Скалы. В ней можно объявлять конструкторы прямо в синтаксисе объявления классов (синтаксис псевдошарпа):
class C(int firstField, string secondField)
{
}
...
var c = C(42, "wow");
Console.WriteLine(c.firstField);
Здесь firstField и secondField это одновременно и поля класса С и параметры его единственного конструктора. Прибавляем сюда наследование и получаем все что нужно для классических конструкторных паттернов. Для старых типов можно использовать аннотацию задаваемую кастом-атрибутами.
В общем, если у них проблемы с дизайном, могу за скромную сумму (ну, скажем 10К зеленых) спроектировать поддержку ПМ для шарпа, что называется под ключ (с грамматикой и описанием семантики).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.