Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
var name = user.NoNull(x => x.Company)
.NoNull(x => x.ParentCompany)
.NoNull(x => x.Name, "N/A");
var total = user.NoNull(x => x.Company)
.DefaultForNull(x => x.Users)
.SelectMany(x => x.Email)
.Where(x => x.EndsWith("@gmail.com"))
.Count();
public static TResult NoNull<TObject, TResult>(this TObject obj, Func<TObject, TResult> accessor, TResult defaultingTo = default(TResult)) {
if (ReferenceEquals(obj, null))
return defaultingTo;
return accessor.Invoke(obj);
}
public static IEnumerable<TResult> DefaultForNull<TObject, TResult>(this TObject obj, Func<TObject, IEnumerable<TResult>> accessor, IEnumerable<TResult> defaultingTo = null) {
if (ReferenceEquals(obj, null))
return defaultingTo ?? Enumerable.Empty<TResult>();
return accessor.Invoke(obj);
}
public int X { get; } = x; совершенно не нужен в свете синтаксиса public int X => x;public int X { get; private set;}public class Point(int x, int y)
{
public int X { get { return x; } }
public int Y { get { return y; } }
public int Dist { get; } = Math.Sqrt(x * x + y * y);
}
public int X { get; private set;} = 100;
private int _x = 100;
public int X { get{ return _x; }; set { _x = value; } }
public int X { get; } = 100;
private readonly int _x = 100;
public int X { get{ return _x; };}
Правда, непонятно зачем это нужно, учитывая, что IEnumerable можно передавать в методы и без ключевого слова params.
void Test(params IEnumerable<int> ints)
Test(1,2,3)Test(any_collection_instance)Test(any_collection_instance.ToArray())public void Test(params int[] arr)
{
}
...
int[] arr1 = new int{1,2,3};
int[] arr2 = new int{3,4,5};
Test(arr1,arr2);//ошибка
var c = match(s)
{
case 1 => "One"
case _ => "Two"
}
class TestClass(int x, int y)
{
int _x = x;
int _y = y;
}
string name = points?.FirstOrDefault()?.Name;
string SX=points?.First()?.X?.toString() ?? "No points";
x=points?.[0]?.X ?? -1;
string SX=points?.First()?.X?.toString() ?? "No points";
var x = point == null ? (int?)null : point.X;
?. — вводят для того, чтобы получать null вместо NullReferenceException. point?.X ?? 3, если X — int, должен быть int)int.TryParse("123", out int x);
Console.WriteLine(x);
Получается какой-то уход от объектно-ориентированного программирования к функциональному.
А вдруг метод будет иметь 5-6-7 параметров? Тогда пол часа будешь искать где же объявлена переменная (если не тыкать F12)
А extension-методы вас не смущали?Да… Было дело… Как-то в чужом коде долго пытался на MSDN найти описание одного extension-метода к одному стандартному фрэймворковскому классу)))) Но ведь речь не об этом. Речь о том, что на NDC предложили, опять же на мой взгляд, язык еще чуточку запутать. Лично я, в отличие от вас, не улавливаю логику связи юзингов и икстеншинов.
К тому же, в C++ унаследованы глобальные методы из C, и ничего, вроде никто не называет это функциональщиной…Ну давайте еще вернем их плюсов директиву препроцессора #include))))
А вот если несколько функциональных класса реализуют метод с одинаковым именем, то потенциально создаются неприятности с копипастом кода)
import static com.foo.myMethod;
myMethod();
А как вывод типов вообще может быть фишкой CLR, а не компилятора?Подробнее в этой статье можно прочитать, как в Java чисто компиляторная фишка, а в C# — рантаймовая.
private delegate void Del1();
private delegate void Del2();
static void Main()
{
Del1 del1 = () => Console.WriteLine("Foo");
Del2 del2 = (Del1) del1;
}
Ну, в случае с IList, это не баг, это фича. Имеются соответствующие свойства, которые показывают, какой перед нами IList: read-only, fixed-size или variable-size.
У нас есть T[], т.е. массив, но он не интерфейс.
Думаю, причина примерно та же, почему мы не можем считать одинаковыми классы:
class Foo { public int _foo; }
class Bar { public int _bar; }
Del2 del2 = del1.Invoke;
string myNotSoCoolString = Foo.GetStringOrNull();
string! myCoolString = myNotSoCoolString; // <- что тут должно быть?
Person GetPerson(Guid), который по договоренности бросает ошибку, если персона не найдена, и возвращает объект, если найдена. null в возврате быть не может. Это все вынесено в интерфейс и реализуется не нами.GetPerson(Guid).Name гарантированно сработает или кинет ошибку известного типа (персона не найдена), а nullref без объяснений мы не встретим никогда. Если же у нас нет статического анализатора, то мы либо должны проверить, что GetPerson вернул не null и самостоятельно кинуть ошибку «ааа, паника, неопределенное поведение», либо быть готовыми к неспецифицированному nullref.И лично я начинаю видеть проблему: Person/Person! означают одну и ту же сущность (некоего человека), но типы оказываются совершенно разными и не всегда взаимозаменяемыми.
Person и Person! соотносятся (должны соотносится при правильном дизайне) так же, как Guid? и Guid — второе неявно приводимо к первому, первое приводимо ко второму через явную предварительную проверку на Null. После этого все последующее ваше рассуждение сводится к (уже существующему) холивару — должно ли быть контрактное ограничение NotNull на возврате GetPerson, или же поведение должно быть как у FindPerson.Но этого и так по факту не происходит
методы GetById они и так самые надежные во всем коде
GetPerson в типичном репозитории://предположим, что this.Persons - это IQueryable<Persons>, откуда он взялся - не важно
Person GetPerson(Guid id)
{
return Persons.Single(p => p.Id == id);
}
//комментарии лида: во время исполнения случится Sequence contains no elements, полжизни будем разбираться, что имелось с виду
//(монад у нас предположительно нет, с ними код проще радикально)
Person GetPerson(Guid id)
{
var person = Persons.SingleOrDefault(p => p.Id == id);
if (person == null)
throw new ArgumentOutOfRangeException();
return person;
}
//лид счастлив
//прошло полгода
//лид сменился
//комментарий нового лида: репозиторий должен всегда возвращать объект или null, проверка на наличие объекта - дело внешнего слоя
//...и переименуйте метод в FindPerson
Person FindPerson(Guid id)
{
return Persons.SingleOrDefault(p => p.Id == id);
}
//лид счастлив, разработчик счастлив, через неделю падает единственное место кода, где нет проверки на null
я и так после них никогда не пишу проверку на null
А в других методах отсутствие объекта это симптом ошибки, а не причина.
разница в затратах на отлов и исправление равна нулю. Другими словами есть сомнения, что код станет надежнее.
Если не может — очевидно, что он бросает исключение.
лучше уж тогда ввести из джавы фукнциональность throws, данный кейс оно покрыло бы
Оператор безопасной навигации (Safe Navigation Operator)
Вывод типа для конструктора generic класса
public Point Move(int dx, int dy) => new Point(X + dx, Y + dy);
public Point Move(int dx, int dy){ return new Point(X + dx, Y + dy); }
public Point Move(int dx, int dy)
=> new Point(X + dx, Y + dy);
public Point Move(int dx, int dy)
{ return new Point(X + dx, Y + dy); }
ни как не даст написать IDE из-за своих попыток его отформатировать, да и выглядит такое написание очень странно. Четыре же строчки отводить на такую функцию — не хочется.)var @switch = new TypeSwitch()
.Case<string>(x => ProcessString(x, ...))
.Case<int>(x => Process(int, ...));
@switch.On(value);
x.As<MyClass>().Do(m => m.Foo());
as, конечно)var myClassX = x as MyClass;
if (myClassX != null) {
myClassX.Foo();
}
class Base {
public void Foo() {
Console.WriteLine("Base.Foo");
}
}
class Derived : Base {
public new void Foo() {
Console.WriteLine("Derived.Foo");
}
}
Base x = new Derived();
if (x is Derived) {
x.Foo(); // вызываем сокрытый Base.Foo, но только если x - Derived
}
interface A
{
void A();
}
interface B
{
void B();
}
//...
A x = new Smth();
x.A(); //работает;
if (x is B)
{
x.B(); //работает
x.A(); //ошибка компиляции
}
Во первых можно не делать смарткасты для интерфейсов, и оставить дял классов (ввиду отсутствия миксинов).
Поверьте, я представляю сколько там камней(вроде совпадения методов в разных интерфейсах), но решать их должен Compiler team, а не балаболы на форумах.
if (x.CastAs(out Foo foo)) ..., или так: if (x.CastAs<Foo>(out var foo)) ...public static T CastAs<T> (this object obj, out T value) {
if (obj is T) {
value = (T)obj;
return true;
} else {
value = default(T);
return false;
}
}
obj.CastAs(out Foo foo) && foo.Parent.CastAs(out Bar bar) && bar.DoSomething();
x.OfType<Foo>().With(foo => foo.Parent).OfType<Bar>().Do(bar => bar.DoSomething)
Будущее C#