Анонимные внутренние классы в 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 два Colors считаются равными, если все их свойства(кроме 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) это не сравнить Colors, как я хочу (используя все свойства, кроме Id), так и равенство Fonts будет протестирован неправильно. Я прав ?

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.


нет, на момент, когда этот вопрос был первоначально написан (C# 3.0), его нет.