Cookies op Tweakers

Tweakers maakt gebruik van cookies, onder andere om de website te analyseren, het gebruiksgemak te vergroten en advertenties te tonen. Door gebruik te maken van deze website, of door op 'Ga verder' te klikken, geef je toestemming voor het gebruik van cookies. Wil je meer informatie over cookies en hoe ze worden gebruikt, bekijk dan ons cookiebeleid.

Meer informatie
Toon posts:

[C#] Bestaat deze language feature?

Pagina: 1
Acties:

  • NickThissen
  • Registratie: november 2007
  • Nu online
Het viel me op dat ik in een MVC project veel code schrijf die volgens mij wat korter kan met een language feature die (voor zover ik weet) niet bestaat. Ik dacht laat ik eens vragen of ik misschien iets over het hoofd zie :) Het duurde bijvoorbeeld ook best lang voordat ik afwist van de nieuwe null-conditional operator. Misschien bestaat er al iets, of misschien is het wel helemaal niet nuttig of uberhaupt mogelijk.

Wat ik voorstel als language feature is eigenlijk een conditional return statement. Als het statement iets (anders dan null) terug geeft dan gedraagt het zich als een normale return (en geeft die waarde terug), maar als er null terug komt dan gaat de code verder (in plaats van terug geven van null).

Een voorbeeld, stel dat ik in een MVC app vaak moet kijken of een gebruiker wel een bepaalde actie mag uitvoeren. Bijvoorbeeld het wijzigen van een "team" (wat dat dan ook is) wat alleen mag als de gebruiker admin rechten heeft voor dat team:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[Authorize]
public ActionResult Edit(int id)
{
    // Zoek team
    Team team = db.Teams.Find(id);
    if (team == null)
        return HttpNotFound();
        
    // Vind gebruiker
    User user = GetUser(); // zie beneden
    
    // Check rechten voor dit team
    if (!IsTeamAdmin(user, team))
        return new HttpUnauthorizedResult();
        
    // Doe de edit
    ...
    
    return View(team);
}

private bool IsTeamAdmin(User user, Team team)
{
    return team.IsAdmin(user);
}

private User GetUser()
{
    return UserManager.FindById(User.Identity.GetUserId());
}


Het gaat om regel 13/14. Dit stukje code komt natuurlijk overal in de controller voor, bij het opvragen van de Edit, dan opnieuw bij het posten van de Edit, hetzelfde voor Delete, toevoegen van andere users, etc.

Ik voorzie eigenlijk een "conditional return" statement, bijvoorbeeld "return?" wat dan als volgt werkt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[Authorize]
public ActionResult Edit(int id)
{
    // Zoek team
    Team team = db.Teams.Find(id);
    if (team == null)
        return HttpNotFound();
        
    // Vind gebruiker
    User user = GetUser(); // zie beneden
    
    // Check rechten voor dit team
    return? CheckIsTeamAdmin(user, team);
        
    // Doe de edit
    ...
    
    return View(team);
}

private ActionResult CheckIsTeamAdmin(User user, Team team)
{
    if (!team.IsAdmin(user))
            return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
    return null;
}

private User GetUser()
{
    return UserManager.FindById(User.Identity.GetUserId());
}


De return statement is nu dus eigenlijk verplaatst naar de CheckIsTeamAdmin functie, met als resultaat dat ik niet elke keer opnieuw de "return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);" hoef te typen.

Het idee is dus dat CheckIsTeamAdmin null terug geeft als de user inderdaad admin is, en dat de code daarna verder gaat na de "return?" regel, in plaats van stopt.


In dit voorbeeld scheelt het natuurlijk niks maar hoe vaker je deze check moet doen hoe meer het scheelt.

Dit bestaat niet, toch? Zou dit een leuke feature zijn, of mis ik iets waardoor dit totaal niet kan werken?

Het enige wat ik kan bedenken dat misschien problemen geeft is het "speciale" gedrag van null terug geven als je de code wil laten verder lopen. Eventueel zou je ook gewoon geen return statement kunnen doen als de conditie niet goed is, dus bijvoorbeeld:
C#:
1
2
3
4
5
6
private conditional ActionResult CheckIsTeamAdmin(User user, Team team)
{
    if (team.IsAdmin(user))
            return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
    // geen return
}

Dit mag natuurlijk nu niet, dus om het de compiler wat makkelijker te maken verzin ik maar een 'conditional' woord in de declaratie van de functie (anders zou ELKE functie in principe legaal zijn zonder return en dat gaat natuurlijk problemen geven).


Zomaar iets waar ik aan zat te denken, heeft het zin om dit als feature aan te dragen ofzo? :+

Mijn iRacing profiel


  • Haan
  • Registratie: februari 2004
  • Laatst online: 16:15

Haan

dotnetter

Dit specifieke geval zou je volgens mij gewoon op moeten lossen met rollen, of anders verwerken in een custom authorization filter, heb je geen nieuwe taalconstructies voor nodig ;)

Kater? Eerst water, de rest komt later
Last.fm profiel


  • The_Ghost16
  • Registratie: januari 2004
  • Laatst online: 26-10 15:16
Volgens mij kun je dit beter oplossen met Attributes. Die zet je boven je methode (waar nu ook [Authorize] staat) en die voeren dan een bepaalde check uit.

Zoals Haan ook zegt kun je ook werken met rollen. Dan krijg je een Administrators rol en een Users rol bijvoorbeeld. Dit kun je dan ook weer checken met het Authorize attribute.

  • NickThissen
  • Registratie: november 2007
  • Nu online
Goed, niet het beste voorbeeld, maar daar gaat het niet om.

Een ander voorbeeld dan;

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public int GetSomeValue()
{
    if (!Check1())
        return 0;
    if (!Check2())
        return 18;
    if (!Check3())
        return 23;
    if (!Check4())
        return new DefaultValuesGetter().DefaultReturnValue;
        
    return 1 + 1;
}

// Of:
public int GetSomeValueConditional()
{
    return? Check1();
    return? Check2();
    return? Check3();
    return? Check4();
    return 1 + 1;
}


Het idee is om de logica van de "check" (in eerste voorbeeld "mag de gebruiker dit doen?") samen te koppelen met de waarde die terug gegeven moet worden als die check niet goed is.

In "GetSomeValue" geven de "Check" functies booleans terug, die ik eerst moet checken en daarna nog moet vertellen wat GetSomeValue dan moet terug geven (als er "false" terug komt).

In "GetSomeValueConditional" geven de "Check" functies meteen de waarde terug die ik terug wil sturen (0, of 18, of 23, of...).

Gewoon ietsje korter, ik denk dat het best handig kan zijn in veel gevallen.

Mijn iRacing profiel


  • RobIII
  • Registratie: december 2001
  • Nu online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

NickThissen schreef op donderdag 17 december 2015 @ 14:49:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public int GetSomeValue()
{
    if (!Check1())
        return 0;
    if (!Check2())
        return 18;
    if (!Check3())
        return 23;
    if (!Check4())
        return new DefaultValuesGetter().DefaultReturnValue;
        
    return 1 + 1;
}

// Of:
public int GetSomeValueConditional()
{
    return? Check1();
    return? Check2();
    return? Check3();
    return? Check4();
    return 1 + 1;
}
Ik vind 't maar raar... je tweede voorbeeld returned helemaal niet de 0, 18, 23, ... values? Of komen die dan spontaan uit CheckX()?

C#:
1
2
3
4
5
6
7
8
public static int GetSomeValue()
{
    return !Check1() ? 0 : 
        !Check2() ? 18 : 
        !Check3() ? 23 : 
        !Check4() ? new DefaultValuesGetter().DefaultReturnValue : 
        1+1; 
}


:?

Verder eens met de rest; dit doe je eerder met attributes. Je zou nog eens kunnen kijken / googlen naar AOP (Aspect Oriented Programming); misschien dat dat je in de juiste richting helpt? Hoewel me nog steeds niet helemaal duidelijk is wat je nou wil denk ik dat dat nog wel eens in de juiste richting kon zijn.

[Voor 27% gewijzigd door RobIII op 17-12-2015 15:15]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij


  • InZane
  • Registratie: oktober 2000
  • Nu online
The_Ghost16 schreef op donderdag 17 december 2015 @ 14:41:
Volgens mij kun je dit beter oplossen met Attributes. Die zet je boven je methode (waar nu ook [Authorize] staat) en die voeren dan een bepaalde check uit.

Zoals Haan ook zegt kun je ook werken met rollen. Dan krijg je een Administrators rol en een Users rol bijvoorbeeld. Dit kun je dan ook weer checken met het Authorize attribute.
Helemaal mee eens. Ik doe meestal zoiets:

C#:
1
[Authorize(Roles = "Admin")]
RobIII schreef op donderdag 17 december 2015 @ 15:04:
[...]

Ik vind 't maar raar... je tweede voorbeeld returned helemaal niet de 0, 18, 23, ... values? Of komen die dan spontaan uit CheckX()?

C#:
1
2
3
4
5
6
7
8
public static int GetSomeValue()
{
    return Check1() ? 0 : 
        Check2() ? 18 : 
        Check3() ? 23 : 
        Check3() ? new DefaultValuesGetter().DefaultReturnValue : 
        1+1; 
}


:?
Klopt inderdaad niks van.
Verder ben ik zelf niet zo'n fan van die 'shorthand if' notatie, al helemaal niet als het om veel condities gaat.

[Voor 36% gewijzigd door InZane op 17-12-2015 15:08]


  • RobIII
  • Registratie: december 2001
  • Nu online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

InZane schreef op donderdag 17 december 2015 @ 15:06:
Verder ben ik zelf niet zo'n fan van die 'shorthand if' notatie, al helemaal niet als het om veel condities gaat.
Ik ook niet; hoewel 't bij korte simpele dingetjes wel handig kan zijn:
C#:
1
return condition ? Color.Red : Color.Blue;

Wordt de condition ingewikkeld of ga je die ternaries (zo heten ze btw ;) ) "nesten" dan wordt 't een ander verhaal en is 't meestal beter de wat 'langere' versie uit te schrijven omwille van leesbaarheid.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Roses are red Violets are blue, Unexpected ‘{‘ on line 32.

Over mij


  • InZane
  • Registratie: oktober 2000
  • Nu online
RobIII schreef op donderdag 17 december 2015 @ 15:12:
[...]

Ik ook niet; hoewel 't bij korte simpele dingetjes wel handig kan zijn:
C#:
1
return condition ? Color.Red : Color.Blue;

Wordt de condition ingewikkeld of ga je die ternaries (zo heten ze btw ;) ) "nesten" dan wordt 't een ander verhaal en is 't meestal beter de wat 'langere' versie uit te schrijven omwille van leesbaarheid.
Ik eventjes niet meer op de naam komen en ik was te lui om te zoeken, maar ik bedoelde ternaries inderdaad :)
In jouw voorbeeld vind ik het gebruik ervan wel acceptabel. Zou ik zelf ook zo doen.

  • Haan
  • Registratie: februari 2004
  • Laatst online: 16:15

Haan

dotnetter

En voor de volledigheid dan maar:
C#:
1
2
3
4
5
// null coalescing operator
var foo = GetFoo() ?? new Foo();

// nieuw in C# 6: Null-Conditional Operator
string x = foo.Name?.ToUpper() // ipv if (foo.Name != null)

Kater? Eerst water, de rest komt later
Last.fm profiel


  • NickThissen
  • Registratie: november 2007
  • Nu online
RobIII schreef op donderdag 17 december 2015 @ 15:04:
[...]

Ik vind 't maar raar... je tweede voorbeeld returned helemaal niet de 0, 18, 23, ... values? Of komen die dan spontaan uit CheckX()?
Precies:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public int? Check1()
{
    if (!SomeCheck())
        return 0;
    return null;
}
public int? Check2()
{
    if (!SomeOtherCheck())
        return 18;
    return null;
}
public int? Check3()
{
    if (!SomeThirdCheck())
        return 23;
    return null;
}

Enzovoorts.

Met de ternary operator kom je nog wel een eind in dit voorbeeld, maar dat is eigenlijk alleen omdat de acties die volgen na alle checks in dit geval maar een regeltje is (return 1 + 1). Stel dat je daar nog een sloot werk moet doen dan past dat niet in een ternary (of je moet dat weer in een aparte method stoppen). De leesbaarheid wordt er niet veel beter van.

Ik vind mijn "return?" nog best leesbaar, als je het leest als "return iets, of anders ga door".

Mijn iRacing profiel


  • R4gnax
  • Registratie: maart 2009
  • Laatst online: 26-10 10:08
NickThissen schreef op donderdag 17 december 2015 @ 14:49:

C#:
1
2
3
4
5
6
7
8
9
// Of:
public int GetSomeValueConditional()
{
  return? Check1();
  return? Check2();
  return? Check3();
  return? Check4();
  return 1 + 1;
}
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Of
public int GetSomeValueConditional()
{
  return
     Check1()
  ?? Check2()
  ?? Check3()
  ?? Check4()
  ?? 1 + 1;
}

// Of
public int GetSomeValueConditional()
{
  new[] { Check1, Check2, Check3, Check4 }
    .Select( fn => fn())
    .Where( x => x != null )
    .FirstOrDefault() ?? 1+1;
}



De laatste is ook eenvoudig om te bouwen naar een herbruikbare methode waarop je van buiten de set Func<T> delegates inbrengt. Zo kun je eenvoudig een strategy pattern implementeren, wat eigenlijk de ge-eikte architectureel verantwoorde oplossing is als je complexe ketens business-logica in opvolging moet af gaan...

C#:
1
2
3
4
5
6
7
8
public static class Strategy {
  public static T Evaluate<T>(T fallback, params Func<T>[] strategies) { 
    return strategies
      .Select( strategy => strategy())
      .Where( result != null )
      .FirstOrDefault() ?? fallback;
  }
}

[Voor 12% gewijzigd door R4gnax op 17-12-2015 21:43]

Pagina: 1


Microsoft Xbox Series X LG CX Google Pixel 5 CES 2020 Samsung Galaxy S20 4G Sony PlayStation 5 Nintendo Switch Lite

Tweakers vormt samen met Hardware Info, AutoTrack, Gaspedaal.nl, Nationale Vacaturebank, Intermediair en Independer DPG Online Services B.V.
Alle rechten voorbehouden © 1998 - 2020 Hosting door True