#c# #.net #generics #architecture
#c# #.net #дженерики #архитектура
Вопрос:
я пытаюсь сгенерировать некоторые методы, подобные приведенным ниже, add crm
и human resources
. Но я хочу написать Crm, HumanResources, SupplChain и т.д. (5-6 модулей) как написать приведенные ниже коды просто (приведенные ниже коды — скелет :)) сначала я писал без дженериков, а затем я написал с дженериками. Второе использование отличается от первого. 1) Четкое чтение также управляемое и расходуемое
2) дает мне некоторые преимущества в производительности. Нет процесса упаковки и распаковки
МОЖЕТЕ ЛИ ВЫ научить меня плюсам и минусам дженериков С сравнением первого и второго использования?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace App.GenericsBaseClass.Diff
{
// View :
class Program
{
static void Main(string[] args)
{
new Crm().Add("Yapı Kredi");
Console.Read();
new HumanResources().Add("yusuf karatoprak");
Console.Read();
}
}
// Business
public class Crm : Customer
{
public override void Add(object obj)
{
if (obj is String)
{
base.Add(obj);
}
else
{
throw new InvalidOperationException("Customer Name must be string Format...");
}
}
}
public class HumanResources : Staff
{
public override void Add(object obj)
{
if (obj is string)
{
base.Add(obj);
}
else
{
throw new InvalidOperationException("Stuff Name must be string Format...");
}
}
}
// Dal
public class Staff
{
public virtual void Add(object obj)
{
new Db.Staff().Save((string)obj);
}
}
public class Customer
{
public virtual void Add(object obj)
{
new Db.Customer().Save((string)obj);
}
}
// Model
public class Db
{
public class Customer
{
public void Save(string Name)
{
Console.WriteLine("Customer : {0} is saved",Name);
}
}
public class Staff
{
public void Save(string Name)
{
Console.WriteLine("Staff: {0} is saved", Name);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace App.GenericsInstadofBaseMethod
{
class Program
{
static void Main(string[] args)
{
new Crm<string>().Add("Yapı Kredi");
Console.Read();
new HumanResources<string>().Add("yusuf karatoprak");
Console.Read();
}
}
// Business
public class Crm<T> : Customer<T>
{
public override void Add(T obj)
{
base.Add(obj);
}
}
public class HumanResources<T> : Staff<T>
{
public override void Add(T obj)
{
base.Add(obj);
}
}
// Dal
public class Staff<T>
{
public virtual void Add(T obj)
{
new Db<T>.Staff().Save(obj);
}
}
public class Customer<T>
{
public virtual void Add(T obj)
{
new Db<T>.Customer().Save(obj);
}
}
// Model
public class Db<T>
{
public class Customer
{
public void Save(T Name)
{
Console.WriteLine("Customer : {0} is saved", Name.ToString());
}
}
public class Staff
{
public void Save(T Name)
{
Console.WriteLine("Staff: {0} is saved", Name);
}
}
}
}
Комментарии:
1. Чего вы пытаетесь достичь с помощью этого использования дженериков? Мне кажется, что использовать дженерики совершенно бесполезно
Name
.2. Вероятно, должно быть
Customer<T>
иStaff<T>
вместоDb<T>
3. Если вы хотите сохранить строковое имя, сделайте так, чтобы ваш метод принимал строку. Какой смысл в создании контракта API, который вы на самом деле не можете выполнить? Вся иерархия типов запутана из-за смешения проблем с сохраняемостью с излишне глубокой иерархией наследования. Я бы рекомендовал взглянуть на приличный ORM.
Ответ №1:
Из того немногого, что я понимаю, большинство операций с дженериками выполняются на IL
уровне, так что потери производительности невелики, если вообще есть. Все, что он делает, это заставляет и проверяет объекты определенного типа. Самым большим недостатком является понимание контравариантности и ковариации. Вот несколько статей об этом.
Статьи
Использование различий в интерфейсах для универсальных коллекций
Часто задаваемые вопросы о ковариации и контравариантности (обновлен .NET 4.0)
Плюсы
С дженериками вы получаете безопасность типов как во время выполнения, так и во время разработки. Вы можете получать информацию intellisense о своих объектах в режиме реального времени, как если бы они были жестко запрограммированы для определенного типа. Это очень полезно при настройке обоих методов расширения, и …ну, почти все остальное. Фактически, начиная с .NET 2.0, я даже не обнаруживаю, что использую не-универсальные списки для чего-либо еще, если только тип не является просто object
- Редактировать
Как указывали другие, это предотвращает необходимость распаковки.
Минусы
Из-за разной природы контраргументации и ковариации иногда может возникнуть путаница в отношении того, что приемлемо, а что нет в дженерике. Например, если вы рассмотрите следующий макет ..
interface IBase {
}
class Alpha : IBase {
}
class Beta : Alpha {
}
IList<IBase> ListOfObjects { get; set; }
Это сбивает с толку. Можете ли вы передать оба Alpha
и Beta
объекты в список? Когда я попробовал это, вы не смогли. Но тогда сработало следующее..
IList<Alpha> ListOfObjects { get; set; }
Он принимал как Alpha
, так и Beta
объекты, потому что Beta
наследует Alpha
Взгляните на дополнительную информацию о контравариантности и ковариации здесь. Это очень полезно.
Я никогда не замечал снижения производительности от дженериков. Однако это не означает, что их не существует. Однако это всего лишь обычные объекты, поэтому я не вижу, где могут быть накладные расходы. В общем случае компилятор не должен даже разрешать вам запускать код, в котором вы пытаетесь добавить неподходящие объекты в общий список, и если это происходит, то вы где-то допустили ошибку разработчика, или используете dynamics
, или столкнулись с проблемой контравариантности или ковариантности. Хотя я понимаю, что кое-что из этого изменилось с .NET 4.0.
Комментарии:
1. на самом деле не все так похоже на шаблоны. И что вы подразумеваете под «старым кодом C «?
2. Разница в терминологии. Профессора моего колледжа давным-давно показали нам кое-что на C , что функционировало немного похоже на дженерики, и назвали их шаблонами. Я не пишу на C , поэтому я не знаю фактического термина. Я должен был быть более конкретным — честно говоря, я даже не помню этого достаточно, чтобы понять, почему я поместил туда этот комментарий. В принципе, вы могли бы создать класс, который мог бы принимать любой тип и выполнять определенные операции над ними, независимо от типа.
Ответ №2:
Помимо моего поста, объясняющего некоторые вещи о дженериках, ваша ситуация здесь не выглядит так, как будто они действительно требуются вообще. Вместо этого вам следует добавить свойство в свой класс.
public class Person {
public string Name { get; set; }
}
public class Customer : Person {
}
public class Staff : Person {
}
public class HumanResources : Staff {
}
public class Db {
public List<Customer> Customers { get; set; }
public List<Staff> Staff { get; set; }
}
public static void Main(){
var db = new Db {
Customers = new List<Customer> { },
Staff = new List<Staff> { }
};
Db.Customers.Add(new Customer { Name = "Primary Customer" } );
Db.Staff.Add(new Staff { Name = "Employee Prime" } );
Db.Staff.Add(new HumanResources { Name = "Human Resource Manager" });
}
Дженерики следует использовать для указания вводимого типа, а не для фактического управления данными. В приведенном вами примере дженерики используются не по назначению.
В этом примере вы создаете базовый класс, а затем три подтипа, один из которых наследуется от другого подтипа. Итак, у вас есть Customer
и Staff
в качестве двух основных типов, которые принимает Db
, (предполагая, что это сокращение от database).
Db
Может содержать список Customer
s и список Staff
, поскольку HumanResources
это тип Staff
, вы можете хранить их вместе. Вы также могли бы добавить List<Person> People { get; set; }
, и он принимал бы все типы, поскольку они неизбежно наследуются друг от друга.
Что касается принудительного использования строк, это было бы лучше сделать в ваших конструкторах.
public class Person {
public Person(string name) {
this.Name = name; }
}
public string Name { get; private set; }
}
В этом примере мы настраиваем Name
поле таким образом, чтобы его могли видеть все, но только изменяли и устанавливали при создании (не совсем лучшая идея, но это демонстрирует суть).
Итак, теперь вы бы добавили новый, Customer
подобный этому ..
Db.Customers.Add( new Customer ( "Ciel" ) );
или новый персонал … Db.Staff.Add( new Staff( "Darin" ) );
.
Ответ №3:
Я не думаю, что разница в производительности существенна.
Что вы действительно получаете, используя дженерики, так это проверку типов во время компиляции. Например, это может помешать вам передать объект неправильного типа (использование object
параметра в этом случае привело бы к ошибкам во время выполнения, если вы ожидаете определенный тип).
Комментарии:
1. Дженерики позволяют избежать боксирования для типов значений, тогда как использование
object
принудительно блокирует.2. Я добавлю, что дженерики создают статические версии методов во время компиляции, а не во время выполнения. Таким образом, не должно быть никакого влияния на производительность.
Ответ №4:
Дженерики — это все о безопасности типов, имхо. Вы можете писать более надежный код, поскольку получаете ошибки во время компиляции вместо проб и ошибок во время выполнения.
Боксирование — это бонус, но вы не должны выбирать дженерики только для того, чтобы избежать боксирования (что звучит как преждевременная оптимизация).
Ответ №5:
Здесь вы можете найти очень интересное видео о преимуществах и недостатках использования дженериков.