Анонимные внутренние классы в C#?
есть ли что-то вроде анонимных внутренних классов (используемых в Java) в C#?
я объясняю, для чего я бы использовал его, например: я объявляю и инициализирую поле типа IDictionary<Person, Account>
и мне нужно написать custom IEqualityComparer<Person>
. Это потому, что я хочу, чтобы два человека рассматривались как равные IDictionary, когда у них одинаковые имена и идентификаторы (а не только идентификаторы по умолчанию). Мне это не понадобится!--6--> в любом другом месте кода.
поэтому я должен объявить новый класс, который реализует IEqualityComparer<Person>
для этого ? В Java я бы использовал анонимный класс, что-то вроде этого(это смешанный синтаксис C# - Java, просто чтобы показать, какую функциональность я ищу):
IDictionry<Person, Account> myDict = new Dictionary<Person, Account>(
new IEqualityComparer<Person>(){
public bool Equals(Person a, Person b){
return a.Id == b.Id && a.Name == b.Name;
}
public int GetHashCode(Person p){
return p.Id.GetHashCode() * p.Name.GetHashCode();
}
});
что-то вроде этого в C# ? Я слишком ленив, чтобы писать новый класс каждый раз, когда мне нужно что-то вроде этого.
Примечание: это синтаксис вопрос. Я знаю, как это написать, но я хочу знать, можно ли сделать код укорачиваться.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
EDIT: как вы сами кодируете подобные случаи ? Вы создаете новый класс для реализации интерфейса или что вы делаете делать ? Может, у тебя есть какой-нибудь трюк, который мне понравится.
редактировать как насчет будущей поддержки анонимных классов, таких как Java ? Вы что-нибудь слышали об этом ?
EDIT: Ну я вижу, мне придется предоставить мой фактический код - не просто пример. Это потому, что я не знаю, сработает ли это с решением скита Джона.
фактическая причина, почему я не просто реализовать Equals(object)
и GetHashCode
в самом классе это класс (сущность), созданный e-r framework из диаграммы модели. Если бы я реализовал его в самом классе, мой код был бы удален из класса(сущности) каждый раз, когда я обновляю модель из базы данных (используя функцию "обновление из базы данных"). Класс на самом деле называется Font
не Person
. Это свойства:
Id: int
FamilyName:string
Size:int
Bold:bool
Italic:bool
Underlined:bool
Striked:bool
Foreground:Color
здесь Color
- это другой класс (сущность), созданный из базы данных.
это свойства цвета:
Id:int
Alpha:byte
Red:byte
Green:byte
Blue:byte
так Я не могу изменить шрифт, ни цвет (если я не хочу переписывать эти изменения снова и снова каждый раз, когда я меняю базу данных), что я хочу, чтобы это Dictionary
:
private IDictionary<Font, Something> cache = new Dictionary<Font, Something>(new SomeEqualityComparer());
и компаратор SomeEqualityComparer
должен гарантировать, что два шрифта будут считаться равными, если и только если все перечисленные выше свойства(кроме Id
) равны. В случае последнего свойства Foreground
два Color
s считаются равными, если все их свойства(кроме Id
) равны.
теперь, если я использую решение, которое любезно рекомендовал мне Джон Скит, я не уверен, что это можно гарантировать. Если бы я использовал что-то вроде:
private IDictionary<Font, Something> cache = new Dictionary<Font, Something>(ProjectionEqualityComparer<Font>.Create
(f => new { f.FontName, f.Size, f.Bold, f.Italic, f.Underlined, f.Striked, f.Foreground});
я бы предположил, что анонимные типы называют Equals(object)
по всем свойствам когда их Equals(object)
называется. Однако, как я не могу переопределить Color
' s Equals(object)
это не сравнить Color
s, как я хочу (используя все свойства, кроме Id
), так и равенство Font
s будет протестирован неправильно. Я прав ?
7 ответов
в последнем редактировании вы упоминаете, что причина, по которой Вы не реализуете Equals и GetHashCode, заключается в том, что код для ваших классов генерируется автоматически, и вы не хотите повторно реализовывать этот код каждый раз при регенерации кода.
Это один из сценариев, по которому частичных классов в C#
многие инструменты генерации кода будут генерировать классы с частичным ключевым словом, чтобы вы могли воспользоваться этой функцией. Проверьте, являются ли классы, создаваемые для вашего кода, частичными.
в отдельном файле (или файлах), который не будет перезаписан при регенерации кода, в той же сборке у вас может быть что-то вроде следующего:
partial class Font
{
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
// ...
}
}
partial class Color
{
public override bool Equals(object obj)
{
// ...
}
public override int GetHashCode()
{
// ...
}
}
у меня есть ProjectionEqualityComparer
класс, который вы можете использовать в MiscUtil. Вы бы использовали такой код:
IEqualityComparer<Person> comparer = ProjectionEqualityComparer<Person>.Create
(p => new { p.Name, p.Id });
Thhat использует тот факт, что анонимные типы имеют соответствующие понятия равенства в когда ProjectionEqualityComparer
предлагается сравнить двух людей для равенства, он будет проецировать каждый на анонимный тип и сравнивать эти экземпляры. Аналогично, когда его просят хэш-код, он будет выполнять проекцию и просить, чтобы его хэш-код.
EDIT: для решения ваших проблема цвета, вы правы: если Color
не переопределяет Equals / GetHashCode так, как вы хотите, вы не можете использовать его напрямую. Тем не менее, вы можете вместо этого:
private IDictionary<Font, Something> cache = new Dictionary<Font, Something>
(ProjectionEqualityComparer<Font>.Create(f => new {
f.FontName, f.Size, f.Bold, f.Italic, f.Underlined, f.Striked,
f.Foreground.Alpha, f.Foreground.Red, f.Foreground.Green,
f.Foreground.Blue});
если вы можете изменить Color
введите с точки зрения свойств, было бы проще, если бы вы могли дать ему свойство ARGB, сгенерированное из других, чтобы вы могли написать:
private IDictionary<Font, Something> cache = new Dictionary<Font, Something>
(ProjectionEqualityComparer<Font>.Create(f => new {
f.FontName, f.Size, f.Bold, f.Italic, f.Underlined, f.Striked,
f.Foreground.ARGB });
это довольно уродливо, но это должно сработать...
нет, нет. Существуют анонимные типы, например
var MyType = new { id=1, name="john", dept = "sales" };
но они очень ограничены и содержат только свойства только для чтения и никаких методов.
буквальный ответ заключается в том, что нет, C# не имеет анонимных внутренних классов, потому что Java добавила их, чтобы обойти отсутствие первоклассных функций, которые есть у C#. Более конкретно, чтобы решить вашу проблему, вы можете просто реализовать IEquatable<Person>
на Person
класс, а потом IDictionary
будет использовать его автоматически. Это наиболее распространенное решение этой проблемы и работает до тех пор, пока вы согласны с процессом сравнения людей, запеченных в этом классе.
если вы хотите логика сравнения/равенства не должна быть привязана непосредственно к Person
, большинство коллекций в .NET позволяют вам пройти в Comparison<T>
object (который является делегатом, а не интерфейсом), позволяя вам делать хорошую логику сортировки на месте. Например, чтобы отсортировать список людей по имени, вы можете сделать:
List<Person> people = ...
people.Sort((x, y) => x.Name.CompareTo(x.y));
к сожалению, Dictionary
не имеет чего-то похожего на функцию равенства. В .NET 4.0 стандартный ответ, похоже, переопределяет EqualityComparer<T>
:
public class PersonComparer : EqualityComparer<Person>
{
public override bool Equals(Person a, Person b)
{
return a.Id == b.Id && a.Name == b.Name;
}
}
определения новый класс каждый раз, когда вам нужно сравнить, хотя, это рутина. Я бы сделал общий, который принимает функцию:
public class Equality<T> : EqualityComparer<T>
{
public Equality(Func<T, T, bool> comparer)
{
this.comparer = comparer;
}
public override bool Equals(T a, T b)
{
return comparer(a, b);
}
private Func<T, T, bool> comparer;
}
добавить небольшой вспомогательный класс:
public static class Equality
{
public static Equality<T> Create<T>(Func<T, T, bool> comparer)
{
return new Equality<T>(comparer);
}
}
и тогда ваше решение становится:
IDictionary<Person, Account> myDict = new Dictionary<Person, Account>(
Equality.Create((a, b) => a.Id == b.Id && a.Name == b.Name);
даже короче, чем на Java.
ближайший ты собираешься сделать это анонимные типы как вы видели в выражении LINQ. Краткий пример из ссылки:
var v = new { Amount = 108, Message = "Hello" };
определенно не то, что вы ищете. Я также не слышал о будущей поддержке анонимных классов в C#.
вы можете определить реализацию интерфейса в одном месте, 1 класс, сопоставить интерфейс с желаемым классом реализации в вашей любимой структуре IOC и вообще не думать о создании экземпляра анонимной реализации 1-time-use.