[Java vs. C#] Многократная реализация классом одного и того же интерфе

kruzer25

Классы могут реализовывать сразу несколько разных интерфейсов; конфликты имён разрешаются с помощью явного указания имени интерфейса перед именем метода.
Если развить эту идею немного дальше - может оказаться, что класс должен реализовывать несколько одинаковых интерфейсов. Если развить ещё дальше - выйдет, что класс должен реализовывать бесконечное количество интерфейсов (например, Double может реализовывать IEquatable со строгой проверкой на равенство, и с параметром опустимого различия между числами, чтобы они ыли признаны равными). Конечно, хочется этого не просто так, а чтобы потом передать на вход куда-то, где ожидают экземпляр этого интерфейса; значит, приведение объекта к нужному типу будет осуществляться не стандартными средствами приведения типов, а, например, у объекта будут методы вроде AsI1, AsI2 etc.
В джаве, насколько я понимаю, всё это замечательно решается через анонимные классы:
interface I {
void DoSomething;
}

class A /*: I*/ {

public string code = "undefined";

public I AsI1 {
return new I {
void DoSomething {
Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with first way");
}
}
}

public I AsI2(string parameter) {
return new I {
void DoSomething {
Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with second way and parameter " + parameter);
}
}
}

}

class Program {

public static void Main {
A obj = new A;
I representations = new I[] { obj.AsI1 obj.AsI2("param") };
int i=1;
foreach(I representation in representations) {
obj.code = "code" + i.ToString;
representation.DoSomething;
i++;
}
}

}

(естественно, код нерабочий, потому что джаву я не знаю).
В C# для этого придётся городить два новых класса (хотя нет, только один новый класс явно реализующих I, принимающих на вход лямбду DoSomething получится очень запутано:
class A {
private class RealisationOfI : I {

private readonly Action _DoSomething;

public RealisationOfI(Action DoSomething) {
this._DoSomething = DoSomething;
}

void I.DoSomething {
this._DoSomething;
}

}

public string code = "undefined";

public I AsI1 {
return new RealisationOfI => Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with first way";
}

public I AsI2(string parameter) {
return new RealisationOfI => Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with second way and parameter " + parameter;
}

}

Если даже упростить задачу, и требовать реализации конечного заранее определённого количества интерфейсов - интуитивно работающий (и, имхо, самый простой из всех трёх примеров) говнокостыль оказывается нерабочим:
class A : I1, I2 {

private interface I1 : I {}
private interface I2 : I {}

public string code = "undefined";

void I1.DoSomething {
Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with first way");
}

void I2.DoSomething {
Console.WriteLine("Object of A with code " + this.code + " is represented as object of I with second way and parameter " + parameter)
}

public I AsI1 {
return (I1)this;
}

public I AsI2 {
return (I2)this;
}

}

то есть, всё равно придётся городить огород с дополнительным классом, что в случае, когда в интерфейсе хотя бы десять методов с кучей параметров, становится уже совершенно ужасным.
Если подумать - интерфейс, в общем-то, ничем не отличается от функтора, просто в интерфейсе их несколько.
Почему же создатели языка не продумали создание объектов, реализующих заданный интерфейс, на лету? Почему не взяли это из джавы (где, насколько я понял, такая фича была с самого начала)?

timefim

Почему же
Потому же, почему не реализован триллиард других более полезных фич.

milanadiana

Еще раз объясни, зачем нужны эти изъебы. Я ни*уя не понял (тут какбы картинка этого летчика на амфетаминах). Объясни в таком виде:
Формулировка задачи --- Решение с необходиостью реализации одного и того же интерфейса.
Причем без всякого кода. Код отвлекает от поиска интуитивных программисту решений.
То, что ты написал на Java очень напоминает паттерн Factory только не для классов, а для интерфейсов.

kruzer25

Формулировка задачи --- Решение с необходиостью реализации одного и того же интерфейса.
Я уже говорил про примеры в первом посте - сравнение даблов может быть реализовано по-разному в зависимости от конкретной ситуации.

milanadiana

Вобщем, я понял. Это действительно фабрика интерфейсов.
А если заменить интерфейс на абстрактный класс, шарпу станет легче?

Devid

Я так понял, что нужен класс реализующий множество одинаковых интерфейсов, параметризованное чем-то (например для сравнения даблов параметр это точность). Почему нельзя добавить этот параметр в класс?

milanadiana

задача такая. Есть интерфейс А. У него есть имплементирующие классы в проекте и т.п.
Он всячески используется в теле некоторой процедуры (только интерфейс но перед использованием необходимо выбрать его реализацию. Если бы для всех реализаций были бы имплементирующие классы, то просто фигарим обычную фабрику и выбираем. Но если необходимы некоторые реализации, которые нигде не имплементированы, или возвращать имплементирующие классы нельзя, потому что они жрут много памяти, то необходимо делать хрень, о которой написал пенартур. Получается что нужно делать двадцать имплементирующих классов и выбирать один из них, а в яве все ограничивается одним классом фабрики.

kruzer25

Почему нельзя добавить этот параметр в класс?
В смысле?
Есть какая-то чужая либа, в которой есть какой-то метод, принимающий на вход объекты, реализующие интерфейс IEquatable. Иногда ты хочешь передавать туда даблы, сравнимые с какой-то точностью, а иногда - сравнимые идеально.

agaaaa

Есть какая-то чужая либа, в которой есть какой-то метод, принимающий на вход объекты, реализующие интерфейс IEquatable. Иногда ты хочешь передавать туда даблы, сравнимые с какой-то точностью, а иногда - сравнимые идеально.
public struct IndistinctDouble: IEquatable<IndistinctDouble>{
public double Value;
... //реализация обёртки с какой-то точностью
}

А вообще во всех уважающих себя либах должна быть перегрузка с IEqualityComparer<T>

6yrop

Если подумать - интерфейс, в общем-то, ничем не отличается от функтора, просто в интерфейсе их несколько.
Почему же создатели языка не продумали создание объектов, реализующих заданный интерфейс, на лету? Почему не взяли это из джавы (где, насколько я понял, такая фича была с самого начала)?
приятно, что мои три дубля с разрывом в полтора года нашли отклик у читателей :D .
Мое предположение, почему не реализовали в C#. Одна из распространенных задач, которую решают анонимные классы в Java — это написание обработчиков событий. В C# для этой задачи ввели специальный синтаксис делегатов и эвентов, который выигрывает у Java-анонимных классов, к тому же естественным образом поддерживает евенты в языках разметки ASP..NET и XAML. А про то, что анонимные классы могут быть полезны не только в эвентах, забыли или просто забили :p .
Можно надеяться, что в новых версиях C# этот промах заметят и введут синтаксис даже лучше, чем в Java — с выводом типов параметров, и с полным замыканием :D . Анонимные классы в Java не поддерживают полного замыкания, в отличие от лямбда выражений C#-а.
Для тех, кто программирует сегодня, рекомендую, не боятся вводить “лишний” интерфейс, там, где в Java напрашивались бы интерфейс + анонимные классы. Реализовывать этот интерфейс простыми вложенными приватными классами с передачей в конструкторе всего что нужно. Это несколько громоздко, но при работе в нормальной IDE это не сильно раздражает. Все известные мне варианты использовать в этих случаях делегаты выглядят жутко неестественно.

kruzer25

public struct IndistinctDouble: IEquatable<IndistinctDouble>{
То есть, заводим обёртку на каждый случай жизни.
Это даже бОльшее говно, чем мой второй вариант (с лямбдами).
А вообще во всех уважающих себя либах должна быть перегрузка с IEqualityComparer<T>
IEquatable было примером.
У тебя все функции, принимающие на вход экземпляр какого-то интерфейса, могут принимать на вход и какой-то совершенно другой объект+описание того, как с ним работать? Интерфейсы, вообще-то, для этого и придумывались.

kruzer25

Анонимные классы в Java не поддерживают полного замыкания, в отличие от лямбда выражений C#-а.
А что в java не так?

6yrop

А что в java не так?
в анонимном классе нельзя обращаться к переменным метода, где объявлен анонимный класс, если переменная не помечена finaly, т.е. типа readonly переменная.

milanadiana

Я думаю, в Sun-е тоже сидят не дураки, обещают и сделают в Java и замыкания, и лямбды. И все будет ка-ра-шо.

kruzer25

в анонимном классе нельзя обращаться к переменным метода, где объявлен анонимный класс, если переменная не помечена finaly, т.е. типа readonly переменная.
Как мне сейчас кажется, это не так уж и страшно.
К приватным полям объекта ведь можно обращаться?

kruzer25

Пусть сначала дженерики нормальные сделают.

katrin2201

Да уже есть Груви, в общем-то.

Devid

Есть какая-то чужая либа, в которой есть какой-то метод, принимающий на вход объекты, реализующие интерфейс IEquatable. Иногда ты хочешь передавать туда даблы, сравнимые с какой-то точностью, а иногда - сравнимые идеально.
А чем плохо сделать обертку над дабл, содержащую значение дабл и точность сравнения. В либу передаем эту обертку, предварительно выставляя точность.

kruzer25

А чем плохо сделать обертку над дабл, содержащую значение дабл и точность сравнения. В либу передаем эту обертку, предварительно выставляя точность
Ты ещё спроси, зачем нужны лямбды, если вместо них можно просто заводить специальные классы (на каждую лямбду по классу) и передавать туда нужные переменные по референсу.

naska79

По сути, в джавском примере сверху объект класса A не реализует интерфейс I (его нельзя передать туда, где принимается I - и это логично, поскольку класс A связан с двумя реализациями и непонятно какую выбирать зато у него есть методы, который возвращает разные объекты разных классов, реализующих I (пусть это классы анонимные, но они есть и их реализация явно описана).
Если нельзя анонимные - ну пусть будут с именами.
По сути ниже - то же самое, хотя отсутствие синтаксического сахара в виде анонимных классов заставляют явно указать
а) имена классов реализаций (что неплохо, ибо реализации чем-то отличаются и это отличие можно коротенько описать в имени)
б) объекты реализаций нуждаются в каких-то данных провайдера реализаций (code).

class A {

public string code = "undefined";

class Impl1_of_I: I
{
A factory;
public Impl1_of_I(A factory) { this.factory = factory; }

public void DoSomething {
Console.WriteLine("Object of A with code " + factory.code + " is provides object of I with first way");
}
};

public I AsI1 {
return new Impl1_of_I(this);
}

class Impl2_of_I: I {
A factory;
string parameter;
public Impl2_of_I(A factory, string parameter) { this.factory = factory; this.parameter = parameter; }
public void DoSomething {
Console.WriteLine("Object of A with code " + factory.code + " provides object of I with second way and parameter " + parameter);
}
};

public I AsI2(string parameter) {
return new Impl2_of_I(this, parameter) ;
}


}


Если же мы хотим, чтобы объект класса A реализовывал интерфейс I, нам надо от него занаследоваться, ну а реализации методов могут зависеть от параметров переданных в конструктор. В этом случае класс A может поддерживать несколько реализаций интерфейса, но объект класса A - только один, в зависимости от его параметров.

agaaaa

IEquatable было примером.У тебя все функции, принимающие на вход экземпляр какого-то интерфейса, могут принимать на вход и какой-то совершенно другой объект+описание того, как с ним работать? Интерфейсы, вообще-то, для этого и придумывались.
В BCL так и сделано. Не понимаю, чем тебя не устраивает.

kruzer25

Что-то я не пойму, а где в твоём примере реализация интерфейса может работать с private членами класса? Или со вложенными классами такое будет работать? (лень проверять)

6yrop

Или со вложенными классами такое будет работать? (лень проверять)
по средством форума penartur осваивал тонкости языка C# :) . Да, такой доступ имеется, в этом то и фишка.

6yrop

Как мне сейчас кажется, это не так уж и страшно.
в реальности это создает проблемы, тут на форуме был тред с примером на эту тему

kruzer25

по средством форума penartur осваивал тонкости языка C#
Тонкости я осваиваю посредством редактора и документации; просто ни разу не надо было из вложенных классов обращаться к внешним.

kruzer25

Ссылку?
Я бы даже сказал, что ты не должен этого хотеть (использовать в лямбде mutable локальную переменную).
Оставить комментарий
Имя или ник:
Комментарий: