Ternary operator difficult to read - c#

Any suggestion how to make the below query more "readable"?
var result = result
.OrderBy(a =>
(conditionA) ?
valueA :
(conditionB ? valueB :
(conditionC ?
(conditionD ?
valueC : valueD) :
valueE)));
Its difficult to read with the long code of condition and value.

There are several ways of improving the readability of your code.
Indentation
One way is to indent the code in a slightly different way, but this only helps readability a little:
var result = result.OrderBy(a =>
conditionA ? valueA :
conditionB ? valueB :
conditionC ? conditionD ? valueC :
valueD :
valueE);
if, else
You could also turn those ternary operators into a more readable chain of if, else.
var result = Result.OrderBy(a => {
if (conditionA)
{
return valueA;
}
else if (conditionB)
{
return valueB;
}
else if (conditionC)
{
if (conditionD)
{
return valueC;
}
else
{
return valueD;
}
}
else
{
return valueE;
}
});
IComparer<>
One option would be to write your own implementation of IComparer<> and pass it to the OrderBy method. I don't know what type your object is or what type the keys are in your code, so I'm going to assume string keys.
public class MyClassComparer : IComparer<MyClass>
{
public int Compare(MyClass x, MyClass y)
{
string xKey = getKey(x);
string yKey = getKey(y);
return string.Compare(xKey, yKey);
}
private string getKey(MyClass item)
{
if (item.conditionA)
{
return item.valueA;
}
else if (item.conditionB)
{
return item.valueB;
}
else if (item.conditionC)
{
if (item.conditionD)
{
return item.valueC;
}
else
{
return item.valueD;
}
}
else
{
return item.valueE;
}
}
}
Extension method
A final option would be to move your code to an extension method:
public static class MyClassExtensions
{
public static string GetSortingKey(this MyClass item)
{
if (item.conditionA)
{
return item.valueA;
}
else if (item.conditionB)
{
return item.valueB;
}
else if (item.conditionC)
{
if (item.conditionD)
{
return item.valueC;
}
else
{
return item.valueD;
}
}
else
{
return item.valueE;
}
}
}
Using the last option, your call to OrderBy is simply:
result.OrderBy(a => a.GetSortingKey())

Related

c# Stabilise complicated sort [duplicate]

I want an alphabetic sort with one exception.
There is a Group with a Name = "Public" and an ID = "0" that I want first.
(would rather use ID = 0)
After that then sort the rest by Name.
This does not return public first.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.Name);
}
}
What I want is:
return GroupAuthoritys.Where(x => x.ID == 0)
UNION
GroupAuthoritys.Where(x => x.ID > 0).OrderBy(x => x.Group.Name);
GroupAuthority has a public property Group and Group has Public properties ID and Name.
I used basically the accepted answer
using System.ComponentModel;
namespace SortCustom
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TestSort();
}
private void TestSort()
{
List<CustomSort> LCS = new List<CustomSort>();
LCS.Add(new CustomSort(5, "sss"));
LCS.Add(new CustomSort(6, "xxx"));
LCS.Add(new CustomSort(4, "xxx"));
LCS.Add(new CustomSort(3, "aaa"));
LCS.Add(new CustomSort(7, "bbb"));
LCS.Add(new CustomSort(0, "pub"));
LCS.Add(new CustomSort(2, "eee"));
LCS.Add(new CustomSort(3, "www"));
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
LCS.Sort();
foreach (CustomSort cs in LCS) System.Diagnostics.Debug.WriteLine(cs.Name);
}
}
public class CustomSort : Object, INotifyPropertyChanged, IComparable<CustomSort>
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, e);
}
private Int16 id;
private string name;
public Int16 ID { get { return id; } }
public String Name { get { return name; } }
public int CompareTo(CustomSort obj)
{
if (this.ID == 0) return -1;
if (obj == null) return 1;
if (obj is CustomSort)
{
CustomSort comp = (CustomSort)obj;
if (comp.ID == 0) return 1;
return string.Compare(this.Name, comp.Name, true);
}
else
{
return 1;
}
}
public override bool Equals(Object obj)
{
// Check for null values and compare run-time types.
if (obj == null) return false;
if (!(obj is CustomSort)) return false;
CustomSort comp = (CustomSort)obj;
return (comp.ID == this.ID);
}
public override int GetHashCode()
{
return (Int32)ID;
}
public CustomSort(Int16 ID, String Name)
{
id = ID;
name = Name;
}
}
}
You need to use a comparison function, they are functions that from two instances of your type return an integer that return 0 if both are equals, a negative value if the first is less than the second and a positive value if the first is greater than the second.
MSDN has a nice table that is easier to follow than text (StackOverflow still doesn't support tables in 2014)
IComparer<T>
Most sort methods accept a custom comparer implementation of type IComparer<T> you should create one encapsulating your custom rules for Group :
class GroupComparer : IComparer<Group>
{
public int Compare(Group a, Group b)
{
if (a != null && b != null && (a.Id == 0 || b.Id == 0))
{
if (a.Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return a.Id == 0 ? -1 : 1;
}
if (a == null || b == null)
{
if (ReferenceEquals(a, b))
{
return 0;
}
return a == null ? -1 : 1;
}
return Comparer<string>.Default.Compare(a.Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _, new GroupAuthorityComparer());
IComparable<T>
If it is the only way to compare Group instances you should make it implement IComparable<T> so that no aditional code is needed if anyone want to sort your class :
class Group : IComparable<Group>
{
...
public int CompareTo(Group b)
{
if (b != null && (Id == 0 || b.Id == 0))
{
if (Id == b.Id)
{
// Mandatory as some sort algorithms require Compare(a, b) and Compare(b, a) to be consistent
return 0;
}
return Id == 0 ? -1 : 1;
}
return Comparer<string>.Default.Compare(Name, b.Name);
}
}
Usage:
items.OrderBy(_ => _.Group);
The choice between one way or the other should be done depending on where this specific comparer is used: Is it the main ordering for this type of item or just the ordering that should be used in one specific case, for example only in some administrative view.
You can even go one level up and provide an IComparable<GroupAuthority> implementation (It's easy once Group implement IComparable<Group>):
class GroupAuthority : IComparable<GroupAuthority>
{
...
public int CompareTo(GroupAuthority b)
{
return Comparer<Group>.Default.Compare(Group, b.Group);
}
}
Usage:
items.OrderBy(_ => _);
The advantage of the last one is that it will be used automatically, so code like: GroupAuthoritys.ToList().Sort() will do the correct thing out of the box.
You can try something like this
list.Sort((x, y) =>
{
if (x.Id == 0)
{
return -1;
}
if (y.Id == 0)
{
return 1;
}
return x.Group.Name.CompareTo(y.Group.Name);
});
Where list is List<T>.
This method takes advantage of custom sort option provided by List<T> using Comparison<T> delegate.
Basically what this method does is, it just adds special condition for comparison when Id, If it is zero it will return a value indicating the object is smaller which makes the object to come in top of the list. If not, it sorts the object using its Group.Name property in ascending order.
public IEnumerable<GroupAuthority> GroupAuthoritysSorted
{
get
{
return GroupAuthoritys.OrderBy(x => x.Group.ID == 0)
.ThenBy(x => x.Group.Name);
}
}

How optimize by lambda expression

I have a very similar function is only one previous report and the other future, how can I optimize and write beautiful?
public bool AnyPreviousReportByGroup(int groupID)
{
if(this.GroupID == groupID)
{
return true;
}
else
{
return PreviousReport.AnyPreviousReportByGroup(groupID);
}
}
public bool AnyNextReportByGroup(int groupID)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return NextReport.AnyNextReportByGroup(groupID);
}
}
The following code is a conciser way of achieving the same thing:
public bool AnyPreviousReportByGroup(int groupID)
{
return this.GroupID == groupID ||
this.PreviousReport != null &&
this.PreviousReport.AnyPreviousReportByGroup(groupID);
}
If you really want to use lambda expressions, here's a possible way:
public bool AnyReportByGroup(int groupID, Func<Report, Report> getOtherReport)
{
if (this.GroupID == groupID)
return true;
Report other = getOtherReport(this);
return other != null &&
other.AnyReportByGroup(groupID, getOtherReport);
}
You could then call this helper method using lambda expressions:
bool anyPrevious = this.AnyReportByGroup(groupID, report => report.PreviousReport);
bool anyNext = this.AnyReportByGroup(groupID, report => report.NextReport);
private bool AnyReportByGroup(int groupID, Func<int, bool> callback)
{
if (this.GroupID == groupID)
{
return true;
}
else
{
return callback(groupID);
}
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => PreviousReport.AnyPreviousReportByGroup(gid));
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, gid => NextReport.AnyNextReportByGroup(gid));
}
But, I hope, that these methods are just a sample, and in your real code they're more complex.
Otherwise, I can't understand, what do you try to optimize.
May be like this
public enum ReportType{ Next, Previous }
public bool AnyReportByGroup(int groupID, ReportType type)
{
if(this.GroupID == groupID)
return true;
else
{
switch(type)
{
case ReportType.Next:
return NextReport.AnyNextReportByGroup(groupID);
case ReportType.Previous:
return NextReport.AnyPreviousReportByGroup(groupID);
}
return false;
}
}
Here's a non-recursive solution, assuming that you want to return false when you run out of reports:
public bool AnyPreviousReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.PreviousReport);
}
public bool AnyNextReportByGroup(int groupID)
{
return GetEventualValue(groupID, r => r.NextReport);
}
public bool GetEventualValue(int groupID, Func<Report, Report> nextReport)
{
Report report = this;
while (report != null && report.GroupID != groupID)
{
report = nextReport(report);
}
return report != null;
}
Standard way to traverse linked lists is like so:
public bool AnyPreviousReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyNextReportByGroup(int groupID)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = item.NextReport;
} while (item != null);
return false;
}
This has a benefit of not creating potentially massive call stacks the way a recusive approach would.
This also fixes your code, where it never returned false, it would just NPE.
Now we can refactor as you requested:
private bool AnyReportByGroup(int groupID, bool searchForward)
{
var item = this;
do
{
if (item.GroupId == groupID)
{
return true;
}
item = searchForward ? item.NextReport : item.PreviousReport;
} while (item != null);
return false;
}
public bool AnyPreviousReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, false);
}
public bool AnyNextReportByGroup(int groupID)
{
return AnyReportByGroup(groupID, true);
}

Resolve property at runtime. C#

I have some class which have some code
public IEnumerable<TypeOne> TypeOne
{
get
{
if (db != null)
{
var col = db.Select<TypeOne>();
if (col.Count > 0) return col;
}
return db2.TypeOne;
}
}
public IEnumerable<TypeTwo> TypeTwo
{
get
{
if (db != null)
{
var col = db.Select<TypeTwo>();
if (col.Count > 0) return col;
}
return db2.TypeTwo;
}
}
So as You can see there is a lot of duplicated Code and there are same property name and item type of enumerable.
I want to call some property of object like "obj.MyProp". And MyProp must be resolved at runtime with some generic or non-generic method. Is it possible?
Slightly incomplete answer but you'll get the general idea:
This is a scenario where you want generics.
public IEnumerable<t> TypeSomething
{
get
{
if (db != null)
{
t col = db.Select<t>();
if (col.Count > 0) return col;
}
return GetDB<t>();
}
}
You'd need to implement GetDB() to return the appropriate db for any given type, but that'd be a single switch (or you can use reflection to find it)
There are a couple of ways to do this. The best is probably a generic method:
public IEnumerable<T> dbSelect<T>() //may need type constraints here
{
return db != null
? db.Select<T>()
: null;
}
public IEnumerable<TypeOne> TypeOne
{
get { return dbSelect<TypeOne> ?? db2.TypeOne; }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return dbSelect<TypeTwo>() ?? db2.TypeTwo; }
}
If your db2 object has a generic Select<T>-type method like db does, it's even easier:
public IEnumerable<T> dbSelect<T>()
{
return db != null
? db.Select<T>()
: db2.Select<T>(); //or db2.GetEntities<T>() or db2.OfType<T> or whatever
}
//Later, in your main code...
var x = dbSelect<TypeOne>();
var y = dbSelect<TypeTwo>();
This will be type safe, considerably faster than reflection, and will work with Intellisense.
You can solve this by using generics:
public IEnumerable<TypeOne> TypeOne
{
get { return GetTable<TypeOne>(); }
}
public IEnumerable<TypeTwo> TypeTwo
{
get { return GetTable<TypeTwo>(); }
}
private IEnumerable<T> GetTable<T>()
{
if (db != null)
{
var col = db.Select<T>();
if (col.Count > 0) return col;
}
return db2.Select<T>();
}

Is it possible to declare a method as a parameter in C#?

For example the main method I want to call is this:
public static void MasterMethod(string Input){
/*Do some big operation*/
}
Usually, I would do something like this this:
public static void StringSelection(int a)
{
if(a == 1)
{
return "if";
}
else
{
return "else";
}
}
MasterMethod(StringSelection(2));
But I want to do something like this:
MasterMethod( a = 2
{
if(a == 1)
{
return "if";
}
else
{
return "else";
}
});
Where 2 is somehow passed into the operation as an input.
Is this possible? Does this have a name?
EDIT:: Please note, the MasterMethod is an API call. I cannot change the parameters for it. I accidentally made a typo on this.
You can do this via delegates in C#:
public static string MasterMethod(int param, Func<int,string> function)
{
return function(param);
}
// Call via:
string result = MasterMethod(2, a =>
{
if(a == 1)
{
return "if";
}
else
{
return "else";
}
});
You can do this with anon delegates:
delegate string CreateString();
public static void MasterMethod(CreateString fn)
{
string something = fn();
/*Do some big operation*/
}
public static void StringSelection(int a)
{
if(a == 1)
{
return "if";
}
else
{
return "else";
}
}
MasterMethod(delegate() { return StringSelection(2); });
anonymous methods
Yes, use a delegate. Which include Lambda Expressions and anonymous methods
I think you're looking for a delegate.

Creating a generic method in C#

I am trying to combine a bunch of similar methods into a generic method. I have several methods that return the value of a querystring, or null if that querystring does not exist or is not in the correct format. This would be easy enough if all the types were natively nullable, but I have to use the nullable generic type for integers and dates.
Here's what I have now. However, it will pass back a 0 if a numeric value is invalid, and that unfortunately is a valid value in my scenarios. Can somebody help me out? Thanks!
public static T GetQueryString<T>(string key) where T : IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
What if you specified the default value to return, instead of using default(T)?
public static T GetQueryString<T>(string key, T defaultValue) {...}
It makes calling it easier too:
var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified
The downside being you need magic values to denote invalid/missing querystring values.
I know, I know, but...
public static bool TryGetQueryString<T>(string key, out T queryString)
What about this? Change the return type from T to Nullable<T>
public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
Convert.ChangeType() doesn't correctly handle nullable types or enumerations in .NET 2.0 BCL (I think it's fixed for BCL 4.0 though). Rather than make the outer implementation more complex, make the converter do more work for you. Here's an implementation I use:
public static class Converter
{
public static T ConvertTo<T>(object value)
{
return ConvertTo(value, default(T));
}
public static T ConvertTo<T>(object value, T defaultValue)
{
if (value == DBNull.Value)
{
return defaultValue;
}
return (T) ChangeType(value, typeof(T));
}
public static object ChangeType(object value, Type conversionType)
{
if (conversionType == null)
{
throw new ArgumentNullException("conversionType");
}
// if it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
// null input returns null output regardless of base type
if (value == null)
{
return null;
}
// it's a nullable type, and not null, which means it can be converted to its underlying type,
// so overwrite the passed-in conversion type with this underlying type
conversionType = Nullable.GetUnderlyingType(conversionType);
}
else if (conversionType.IsEnum)
{
// strings require Parse method
if (value is string)
{
return Enum.Parse(conversionType, (string) value);
}
// primitive types can be instantiated using ToObject
else if (value is int || value is uint || value is short || value is ushort ||
value is byte || value is sbyte || value is long || value is ulong)
{
return Enum.ToObject(conversionType, value);
}
else
{
throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " +
"not supported for enum conversions.", conversionType.FullName));
}
}
return Convert.ChangeType(value, conversionType);
}
}
Then your implementation of GetQueryString<T> can be:
public static T GetQueryString<T>(string key)
{
T result = default(T);
string value = HttpContext.Current.Request.QueryString[key];
if (!String.IsNullOrEmpty(value))
{
try
{
result = Converter.ConvertTo<T>(value);
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
You can use sort of Maybe monad (though I'd prefer Jay's answer)
public class Maybe<T>
{
private readonly T _value;
public Maybe(T value)
{
_value = value;
IsNothing = false;
}
public Maybe()
{
IsNothing = true;
}
public bool IsNothing { get; private set; }
public T Value
{
get
{
if (IsNothing)
{
throw new InvalidOperationException("Value doesn't exist");
}
return _value;
}
}
public override bool Equals(object other)
{
if (IsNothing)
{
return (other == null);
}
if (other == null)
{
return false;
}
return _value.Equals(other);
}
public override int GetHashCode()
{
if (IsNothing)
{
return 0;
}
return _value.GetHashCode();
}
public override string ToString()
{
if (IsNothing)
{
return "";
}
return _value.ToString();
}
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>(value);
}
public static explicit operator T(Maybe<T> value)
{
return value.Value;
}
}
Your method would look like:
public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible
{
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
return new Maybe<T>();
}
}
return new Maybe<T>();
}
I like to start with a class like this
class settings
{
public int X {get;set;}
public string Y { get; set; }
// repeat as necessary
public settings()
{
this.X = defaultForX;
this.Y = defaultForY;
// repeat ...
}
public void Parse(Uri uri)
{
// parse values from query string.
// if you need to distinguish from default vs. specified, add an appropriate property
}
This has worked well on 100's of projects. You can use one of the many other parsing solutions to parse values.

Categories