C # vs Java Enum (для тех, кто не знаком с C #)
Я программировал на Java некоторое время и просто получил бросок на проект, написанный полностью на C #. Я пытаюсь приблизиться к скорости на C # и заметил enums, используемые в нескольких местах в моем новом проекте, но на первый взгляд enums C # выглядят более упрощенными, чем реализация Java 1.5+. Может ли кто-нибудь перечислить различия между C # и enumsми Java и как преодолеть различия? (Я не хочу запускать языческую пламенную войну, я просто хочу знать, как делать некоторые вещи на C #, которые я использовал в Java). Например, может ли кто-нибудь отправить экземпляр C # на знаменитый пример переименования Sun?
public enum Planet { MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6), EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6), JUPITER (1.9e+27, 7.1492e7), SATURN (5.688e+26, 6.0268e7), URANUS (8.686e+25, 2.5559e7), NEPTUNE (1.024e+26, 2.4746e7), PLUTO (1.27e+22, 1.137e6); private final double mass; // in kilograms private final double radius; // in meters Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } public double mass() { return mass; } public double radius() { return radius; } // universal gravitational constant (m3 kg-1 s-2) public static final double G = 6.67300E-11; public double surfaceGravity() { return G * mass / (radius * radius); } public double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); } } // Example usage (slight modification of Sun's example): public static void main(String[] args) { Planet pEarth = Planet.EARTH; double earthRadius = pEarth.radius(); // Just threw it in to show usage // Argument passed in is earth Weight. Calculate weight on each planet: double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight/pEarth.surfaceGravity(); for (Planet p : Planet.values()) System.out.printf("Your weight on %s is %f%n", p, p.surfaceWeight(mass)); } // Example output: $ java Planet 175 Your weight on MERCURY is 66.107583 Your weight on VENUS is 158.374842 [etc ...]
- Могу ли я избежать использования значения enums при попытке использовать или вернуть его?
- Как создать таблицу, соответствующую перечислению в EF6 Code First?
- Можете ли вы просмотреть все значения enums?
- Наиболее распространенные побитовые операции C # на enumsх
- Зачем использовать оператор Bitwise-Shift для значений в определении enum C?
Перечисления в CLR называются просто константами. Основной тип должен быть неотъемлемым. В Java перечисление больше похоже на именованный экземпляр типа. Этот тип может быть довольно сложным и, как показывает ваш пример, содержать несколько полей различных типов.
Чтобы перенести пример на C #, я просто изменил бы перечисление на неизменяемый class и выставлял бы статические экземпляры только для чтения этого classа:
using System; using System.Collections.Generic; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Planet planetEarth = Planet.MERCURY; double earthRadius = pEarth.Radius; // Just threw it in to show usage double earthWeight = double.Parse("123"); double earthMass = earthWeight / pEarth.SurfaceGravity(); foreach (Planet p in Planet.Values) Console.WriteLine($"Your weight on {p} is {p.SurfaceWeight(mass)}"); Console.ReadKey(); } } public class Planet { public static readonly Planet MERCURY = new Planet("Mercury", 3.303e+23, 2.4397e6); public static readonly Planet VENUS = new Planet("Venus", 4.869e+24, 6.0518e6); public static readonly Planet EARTH = new Planet("Earth", 5.976e+24, 6.37814e6); public static readonly Planet MARS = new Planet("Mars", 6.421e+23, 3.3972e6); public static readonly Planet JUPITER = new Planet("Jupiter", 1.9e+27, 7.1492e7); public static readonly Planet SATURN = new Planet("Saturn", 5.688e+26, 6.0268e7); public static readonly Planet URANUS = new Planet("Uranus", 8.686e+25, 2.5559e7); public static readonly Planet NEPTUNE = new Planet("Neptune", 1.024e+26, 2.4746e7); public static readonly Planet PLUTO = new Planet("Pluto", 1.27e+22, 1.137e6); public static IEnumerable Values { get { yield return MERCURY; yield return VENUS; yield return EARTH; yield return MARS; yield return JUPITER; yield return SATURN; yield return URANUS; yield return NEPTUNE; yield return PLUTO; } } public string Name { get; private set; } public double Mass { get; private set; } public double Radius { get; private set; } Planet(string name, double mass, double radius) => (Name, Mass, Radius) = (name, mass, radius); // Wniversal gravitational constant (m3 kg-1 s-2) public const double G = 6.67300E-11; public double SurfaceGravity() => G * mass / (radius * radius); public double SurfaceWeight(double other) => other * SurfaceGravity(); public override string ToString() => name; } }
В C # вы можете определить методы расширения для перечислений, и это компенсирует некоторые недостающие функции.
Вы можете определить Planet
как перечисление, а также методы расширения, эквивалентные методам surfaceGravity()
и surfaceWeight()
.
Я использовал пользовательские атрибуты, как предложил Михаил , но то же самое можно было бы достичь с помощью Словаря.
using System; using System.Reflection; class PlanetAttr: Attribute { internal PlanetAttr(double mass, double radius) { this.Mass = mass; this.Radius = radius; } public double Mass { get; private set; } public double Radius { get; private set; } } public static class Planets { public static double GetSurfaceGravity(this Planet p) { PlanetAttr attr = GetAttr(p); return G * attr.Mass / (attr.Radius * attr.Radius); } public static double GetSurfaceWeight(this Planet p, double otherMass) { return otherMass * p.GetSurfaceGravity(); } public const double G = 6.67300E-11; private static PlanetAttr GetAttr(Planet p) { return (PlanetAttr)Attribute.GetCustomAttribute(ForValue(p), typeof(PlanetAttr)); } private static MemberInfo ForValue(Planet p) { return typeof(Planet).GetField(Enum.GetName(typeof(Planet), p)); } } public enum Planet { [PlanetAttr(3.303e+23, 2.4397e6)] MERCURY, [PlanetAttr(4.869e+24, 6.0518e6)] VENUS, [PlanetAttr(5.976e+24, 6.37814e6)] EARTH, [PlanetAttr(6.421e+23, 3.3972e6)] MARS, [PlanetAttr(1.9e+27, 7.1492e7)] JUPITER, [PlanetAttr(5.688e+26, 6.0268e7)] SATURN, [PlanetAttr(8.686e+25, 2.5559e7)] URANUS, [PlanetAttr(1.024e+26, 2.4746e7)] NEPTUNE, [PlanetAttr(1.27e+22, 1.137e6)] PLUTO }
В C # атрибуты могут использоваться с enumsми. Хороший пример этого шаблона программирования с подробным описанием здесь (Codeproject)
public enum Planet { [PlanetAttr(3.303e+23, 2.4397e6)] Mercury, [PlanetAttr(4.869e+24, 6.0518e6)] Venus }
Изменить: этот вопрос недавно был задан снова и ответил Jon Skeet: Что эквивалентно перечислению Java в C #? Частные внутренние classы в C # – почему они не используются чаще?
Редактировать 2: см. Принятый ответ, который расширяет этот подход очень блестящим образом!
Переменные Java – это фактически полные classы, которые могут иметь частный конструктор и методы и т. Д., Тогда как enums C # просто называются целыми числами. Внедрение IMO Java намного превосходит.
Эта страница должна помочь вам во время обучения c #, выходящего из лагеря java. (Ссылка указывает на различия в enumsх (прокрутка вверх / вниз для других вещей)
Что-то вроде этого я думаю:
public class Planets { public static readonly Planet MERCURY = new Planet(3.303e+23, 2.4397e6); public static readonly Planet VENUS = new Planet(4.869e+24, 6.0518e6); public static readonly Planet EARTH = new Planet(5.976e+24, 6.37814e6); public static readonly Planet MARS = new Planet(6.421e+23, 3.3972e6); public static readonly Planet JUPITER = new Planet(1.9e+27, 7.1492e7); public static readonly Planet SATURN = new Planet(5.688e+26, 6.0268e7); public static readonly Planet URANUS = new Planet(8.686e+25, 2.5559e7); public static readonly Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7); public static readonly Planet PLUTO = new Planet(1.27e+22, 1.137e6); } public class Planet { public double Mass {get;private set;} public double Radius {get;private set;} Planet(double mass, double radius) { Mass = mass; Radius = radius; } // universal gravitational constant (m3 kg-1 s-2) private static readonly double G = 6.67300E-11; public double SurfaceGravity() { return G * Mass / (Radius * Radius); } public double SurfaceWeight(double otherMass) { return otherMass * SurfaceGravity(); } }
Или объедините константы в class Planet
как указано выше
Перечисление Java представляет собой синтаксический сахар, чтобы представить enums в форме OO. Это абстрактные classы, расширяющие class Enum в Java, и каждое значение enums похоже на статическую реализацию публичного экземпляра classа enum. Посмотрите на сгенерированные classы, а для enums «Foo» с 10 значениями вы увидите «Foo $ 1» через созданные classы Foo $ 10.
Я не знаю C #, хотя я могу только предположить, что перечисление на этом языке больше похоже на традиционное перечисление в языках стиля C. Из быстрого поиска Google я вижу, что они могут содержать несколько значений, поэтому они, вероятно, реализованы аналогичным образом, но с гораздо большими ограничениями, чем позволяет компилятор Java.
Переменные Java позволяют легко преобразовывать типы имен из имени с помощью метода valueOf, созданного компилятором, т.е.
// Java Enum has generics smarts and allows this Planet p = Planet.valueOf("MERCURY");
Эквивалент для необработанного enums в C # является более подробным:
// C# enum - bit of hoop jumping required Planet p = (Planet)Enum.Parse(typeof(Planet), "MERCURY");
Однако, если вы спуститесь по маршруту, предоставленному Kent, вы можете легко реализовать метод ValueOf
в своем classе enum.
Я подозреваю, что enums на C # – это только константы, внутренние для CLR, но не знакомые с ними. Я декомпилировал некоторые classы в Java, и я могу сказать, что вы хотите, чтобы Enums были конвертированы.
Java делает что-то подлый. Он рассматривает class enum как обычный class, насколько это возможно, используя множество макросов при ссылке на значения enum. Если у вас есть оператор case в classе Java, который использует enums, он заменяет ссылки enums на целые числа. Если вам нужно перейти к строке, он создает массив строк, индексированных по порядку, который он использует в каждом classе. Я подозреваю, что сэкономить на боксе.
Если вы загрузите этот декомпилятор, вы увидите, как он создает свой class, интегрирует его. Чрезвычайно увлекательный, чтобы быть честным. Раньше я не использовал class enum, потому что я думал, что он должен раздуваться только для массива констант. Мне нравится это лучше, чем ограниченный способ использования их в C #.
http://members.fortunecity.com/neshkov/dj.html – Java-декомпилятор
Вот еще одна интересная идея, которая отвечает за пользовательское поведение, доступное на Java. Я придумал следующий базовый class Enumeration
:
public abstract class Enumeration where T : Enumeration { protected static int nextOrdinal = 0; protected static readonly Dictionary> byOrdinal = new Dictionary>(); protected static readonly Dictionary> byName = new Dictionary>(); protected readonly string name; protected readonly int ordinal; protected Enumeration(string name) : this (name, nextOrdinal) { } protected Enumeration(string name, int ordinal) { this.name = name; this.ordinal = ordinal; nextOrdinal = ordinal + 1; byOrdinal.Add(ordinal, this); byName.Add(name, this); } public override string ToString() { return name; } public string Name { get { return name; } } public static explicit operator int(Enumeration obj) { return obj.ordinal; } public int Ordinal { get { return ordinal; } } }
У него есть параметр типа в основном, так что порядковый счет будет работать правильно в разных производных enumsх. Пример Operator
Джона Скита из его ответа на другой вопрос (http://stackoverflow.com/questions/1376312/whats-the-equivalent-of-javas-enum-in-c) выше:
public class Operator : Enumeration { public static readonly Operator Plus = new Operator("Plus", (x, y) => x + y); public static readonly Operator Minus = new Operator("Minus", (x, y) => x - y); public static readonly Operator Times = new Operator("Times", (x, y) => x * y); public static readonly Operator Divide = new Operator("Divide", (x, y) => x / y); private readonly Func op; // Prevent other top-level types from instantiating private Operator(string name, Func op) :base (name) { this.op = op; } public int Execute(int left, int right) { return op(left, right); } }
Это дает несколько преимуществ.
- Ординарная поддержка
- Преобразование в
string
иint
что делает возможным выполнение операторов switch - GetType () даст тот же результат для каждого из значений производного типа enums.
- Статические методы из
System.Enum
могут быть добавлены в базовый class Enumeration, чтобы обеспечить ту же функциональность.
Перечисление в Java намного сложнее, чем переполнение C # и, следовательно, более мощное. Поскольку это всего лишь еще один синтаксический сахар для компиляции, мне интересно, стоит ли включать язык, учитывая его ограниченное использование в реальных приложениях. Иногда это сложнее сохранить материал из языка, чем отказаться от давления, чтобы включить второстепенную функцию.
//Review the sample enum below for a template on how to implement a JavaEnum. //There is also an EnumSet implementation below. public abstract class JavaEnum : IComparable { public static IEnumerable Values { get { throw new NotImplementedException("Enumeration missing"); } } public readonly string Name; public JavaEnum(string name) { this.Name = name; } public override string ToString() { return base.ToString() + "." + Name.ToUpper(); } public int CompareTo(object obj) { if(obj is JavaEnum) { return string.Compare(this.Name, ((JavaEnum)obj).Name); } else { throw new ArgumentException(); } } //Dictionary values are of type SortedSet private static Dictionary enumDictionary; public static SortedSet RetrieveEnumValues () where T : JavaEnum { if(enumDictionary == null) { enumDictionary = new Dictionary(); } object enums; if(!enumDictionary.TryGetValue(typeof(T), out enums)) { enums = new SortedSet (); FieldInfo[] myFieldInfo = typeof(T).GetFields(BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.Public); foreach(FieldInfo f in myFieldInfo) { if(f.FieldType == typeof(T)) { ((SortedSet )enums).Add((T)f.GetValue(null)); } } enumDictionary.Add(typeof(T), enums); } return (SortedSet )enums; } } //Sample JavaEnum public class SampleEnum : JavaEnum { //Enum values public static readonly SampleEnum A = new SampleEnum("A", 1); public static readonly SampleEnum B = new SampleEnum("B", 2); public static readonly SampleEnum C = new SampleEnum("C", 3); //Variables or Properties common to all enums of this type public int int1; public static int int2 = 4; public static readonly int int3 = 9; //The Values property must be replaced with a call to JavaEnum.generateEnumValues() to generate an IEnumerable set. public static new IEnumerable Values { get { foreach(var e in JavaEnum.RetrieveEnumValues ()) { yield return e; } //If this enum should compose several enums, add them here //foreach(var e in ChildSampleEnum.Values) { // yield return e; //} } } public SampleEnum(string name, int int1) : base(name) { this.int1 = int1; } } public class EnumSet : SortedSet where T : JavaEnum { // Creates an enum set containing all of the elements in the specified element type. public static EnumSet AllOf(IEnumerable values) { EnumSet returnSet = new EnumSet (); foreach(T item in values) { returnSet.Add(item); } return returnSet; } // Creates an enum set with the same element type as the specified enum set, initially containing all the elements of this type that are not contained in the specified set. public static EnumSet ComplementOf(IEnumerable values, EnumSet set) { EnumSet returnSet = new EnumSet (); foreach(T item in values) { if(!set.Contains(item)) { returnSet.Add(item); } } return returnSet; } // Creates an enum set initially containing all of the elements in the range defined by the two specified endpoints. public static EnumSet Range(IEnumerable values, T from, T to) { EnumSet returnSet = new EnumSet (); if(from == to) { returnSet.Add(from); return returnSet; } bool isFrom = false; foreach(T item in values) { if(isFrom) { returnSet.Add(item); if(item == to) { return returnSet; } } else if(item == from) { isFrom = true; returnSet.Add(item); } } throw new ArgumentException(); } // Creates an enum set initially containing the specified element(s). public static EnumSet Of(params T[] setItems) { EnumSet returnSet = new EnumSet (); foreach(T item in setItems) { returnSet.Add(item); } return returnSet; } // Creates an empty enum set with the specified element type. public static EnumSet NoneOf() { return new EnumSet (); } // Returns a copy of the set passed in. public static EnumSet CopyOf(EnumSet set) { EnumSet returnSet = new EnumSet (); returnSet.Add(set); return returnSet; } // Adds a set to an existing set. public void Add(EnumSet enumSet) { foreach(T item in enumSet) { this.Add(item); } } // Removes a set from an existing set. public void Remove(EnumSet enumSet) { foreach(T item in enumSet) { this.Remove(item); } } }
Вы также можете использовать class утилиты для каждого типа enums, который содержит экземпляр с расширенными данными для каждого значения enums.
public enum Planet { MERCURY, VENUS } public class PlanetUtil { private static readonly IDictionary PLANETS = new Dictionary