I am looking for a solution to concatenate two string values and get null as a result if both are null.
None of string1 + string2, string.Concat(string1, string2), string.Join(string1, string2) work. Research shows that is due to the fact, that these methods internally treat null as empty string.
How to solve this?
Since your actual formula is
(a + b) ?? (c + d) // assumes that null + null == null in (a + b)
I suggest rewriting it into
a == null && b == null ? c + d : a + b
which provides the expected result:
if both a and b are null we have c + d
a + b otherwise
If you want to have null (not empty string) when all a, b, c, d are null:
a == null && b == null ?
c == null && d == null
? null
: c + d
: a + b;
Something like this?
public class StringTest
{
public string CustomConcat(string one, string two) =>
one == null && two == null
? null
: string.Concat(one, two);
[Test]
public void ConcatTest()
{
Assert.IsNull(CustomConcat(null, null));
Assert.AreEqual("one", CustomConcat("one", null));
Assert.AreEqual("two", CustomConcat(null, "two"));
Assert.AreEqual("onetwo", CustomConcat("one", "two"));
// finally, a test for (a + b) ?? (c + d)
Assert.AreEqual("threefour", CustomConcat(null, null) ?? CustomConcat("three", "four"));
}
}
Yes, different method calls treat different preconditions, but you can always create your own function to handle "custom" preconditions.
As the most simple option you can try something like this:
String concatenateStrings(string s1, string s1) {
return (s1 == null && s2 == null)? null : String.concat(s1,s2);
}
You could do this:
var textResult = string.Empty;
if (string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2))
{
textResult = null;
}
else
if (!string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2))
{
textResult = text1;
}
else
if (string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2))
{
textResult = text2;
}
textResult = $"{text1} {text2}";
or in a better way:
textResult = (!string.IsNullOrWhiteSpace(text1) &&
string.IsNullOrWhiteSpace(text2)) ?
text1 :
(string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2)) ?
text2 :
(string.IsNullOrWhiteSpace(text1) &&
!string.IsNullOrWhiteSpace(text2)) ?
text2 :
$"{text1} {text2}";
Related
This question already has answers here:
Is this a bug in Visual Studio 2010 compiler?
(7 answers)
AppSettings value is returning null in ternary operator
(1 answer)
Closed 3 years ago.
I have the following objects:
class Parent
{
public Child Child { get; set; }
}
class Child
{
public string Type { get; set; }
public int Value { get; set; }
}
Why does the following code throw a NullReferenceException?
var value = parent.Child != null &&
parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2;
I would think the null-check would block the second part of the conditional from executing.
However the following code works:
var value = (parent.Child != null) &&
(parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2);
So I think some operator precedence is at fault here...
Why wouldn't it throw a NullReferenceException?
var value = parent.Child != null &&
parent.Child.Type == "test"
? parent.Child.Value == 1
: parent.Child.Value == 2;
Let's rewrite your code with an interim variable:
bool condition = parent.Child != null && parent.Child.Type == "test";
var value = condition ? parent.Child.Value == 1 : parent.child.Value == 2;
And then with an if statement to make it even clearer:
bool value = false;
bool condition = parent.Child != null && parent.Child.Type == "test";
if (condition)
{
value = parent.Child.Value == 1;
}
else
{
vaue = parent.Child.Value == 2;
}
Your condition requires two things to be true: parent.Child isn't null, and parent.Child.Type is equal to "test".
If your condition is false, you use the "else" part of the ternary operator: parent.Child.Value == 2.
If your condition is false because parent.Child == null then you will be trying to access a null object's property in your else section, thus triggering a NullReferenceException.
As for why it's interpreted in this order, see this documentation page about C# operators and their precedence. As you can see, a && b has higher precedence than a ? b : c. a && b will therefore be executed before a ? b : c.
I am using below LINQ Query, now i want to if else condition inside LINQ Query like below- how i can achieve the same ?
if(stemming)
highlightedText = c.Value.p_content != null && c.Value.p_content[0] != null ? c.Value.p_content[0] : string.Empty
if(phoentic)
highlightedText = c.Value.s_content != null && c.Value.s_content[0] != null ? c.Value.s_content[0] : string.Empty
if(content)
highlightedText = c.Value.content != null && c.Value.content[0] != null ? c.Value.content[0] : string.Empty
Complete code -
var highlightedDataLst = objJson.highlighting.Select(c =>
new finalOutput
{
highlightedKey = c.Key,
highlightedText = c.Value.content != null && c.Value.content[0] != null ? c.Value.content[0] : string.Empty
}).ToList<finalOutput>();
Hmm taking a stab at it, it might look something like this:
var highlightedDataLst = objJson.highlighting.Select(c =>
new finalOutput
{
highlightedKey = c.Key,
highlightedText = (stemming ? c.Value.p_content?[0] :
(phoentic ? c.Value.s_content?[0] :
(content ? c.Value.content?[0] : null))) ?? ""
}).ToList<finalOutput>();
You can simplify your conditions using the null conditional operator (?[]) and null coalescing operators (??).
I have a table that I am filtering on.
There is a filter for values 'include' which can be true or false.
I have a filter that has 3 options: true, false, & all.
So, when the filter is true, it should return rows where include = 'true'; when the filter is 'false', return where include = false; and when 'all' return where include = true or false.
Here is my code, that is not working, but I think it should be.
private ICollection<AggregationEntityViewModel> getEntities(AggregationPracticeDetailsViewModel apdvm)
{
bool? filterInclude = Convert.ToBoolean(apdvm.Filter_IncludeValue);
var a = (from e in _repository.GetAll<Entity>()
where e.include == filterInclude != null ? (bool)filterInclude : (true || false)
select e
return a;
}
It is currently returning 0 rows when filter is set to 'All' or 'False', and returning all rows when set to 'Yes'.
FYI, I have ommitted lots of code for clarity's sake.
Please help...thanks!
*EDIT: I've displayed all the code, so you can see why I want to keep it all in linq query. Thanks for all the offered solutions. I see that most solutions involve using Linq Extension methods. Is there anyway to do it in inline linq query? *
bool? filterInclude = Convert.ToBoolean(apdvm.Filter_IncludeValue);
var a = (from e in _repository.GetAll<Entity>()
from u in e.Users
where (e.AuditQuestionGroupId != null ? e.AuditQuestionGroupId : 0) == this.LoggedInEntity.AuditQuestionGroupId
&& e.BatchNumber != null && e.BatchNumber.StartsWith(apdvm.Filter_BatchNumber == null ? "" : apdvm.Filter_BatchNumber)
&& e.Name != null && e.Name.ToLower().StartsWith(apdvm.Filter_EntityName.ToLower())
&& e.EntityState != null && e.EntityState.ToLower().Contains(apdvm.Filter_StateValue == null ? "" : apdvm.Filter_StateValue.ToLower())
&& u.NIAMembershipId != null && u.NIAMembershipId.Contains(apdvm.Filter_MemberNo == null ? "" : apdvm.Filter_MemberNo)
from p in e.PracticeProfiles.DefaultIfEmpty()
join ea in _repository.GetAll<EntityAggregate>() on e.EntityId equals ea.EntityId into eas
from ea in eas.DefaultIfEmpty()
where ea.include == filterInclude != null ? (bool)filterInclude : (true || false)
group e by new { entity = e, profile = p, ea = ea } into newGroup
orderby newGroup.Key.entity.Name
select new AggregationEntityViewModel()
{
Id = newGroup.Key.ea == null ? 0 : newGroup.Key.ea.Id,
EntityId = newGroup.Key.entity.EntityId,
Include = newGroup.Key.ea == null ? (true || false) : (bool)newGroup.Key.ea.include,
BHAddress = newGroup.Key.profile == null || newGroup.Key.profile.soloOffice == null ? false : (bool)newGroup.Key.profile.soloOffice,
Incorporated = newGroup.Key.profile == null || newGroup.Key.profile.company == null ? false : (bool)newGroup.Key.profile.company,
MajorityOwned = newGroup.Key.profile == null || newGroup.Key.profile.capital == null ? false : (bool)newGroup.Key.profile.capital,
MajorityVoting = newGroup.Key.profile == null || newGroup.Key.profile.votingRights == null ? false : (bool)newGroup.Key.profile.votingRights,
Name = newGroup.Key.entity.Name,
Partnership = newGroup.Key.profile == null || newGroup.Key.profile.partnership == null ? false : (bool)newGroup.Key.profile.partnership,
PublicAccountant = newGroup.Key.profile == null || newGroup.Key.profile.publicAccountant == null ? false : (bool)newGroup.Key.profile.publicAccountant,
Trust = newGroup.Key.profile == null || newGroup.Key.profile.operatingTrust == null ? false : (bool)newGroup.Key.profile.operatingTrust,
TrustDeed = newGroup.Key.profile == null || newGroup.Key.profile.deed == null ? false : (bool)newGroup.Key.profile.deed
}).ToList();
return a;
Convert.ToBoolean returns bool, not bool?, so there is no way filterInclude != null is true.
You should use following pattern instead of ternary operator within where clause:
var query = _repository.GetAll<Entity>();
if (apdvm.Filter_IncludeValue == "true")
query = query.Where(x => x.include == true);
else if (apdvm.Filter_IncludeValue == "false")
query = query.Where(x => x.include == false);
return query;
I assumed apdvm.Filter_IncludeValue is a string (and that's why you tried to call Convert.ToBoolean on it).
You could use
private ICollection<AggregationEntityViewModel> getEntities(
AggregationPracticeDetailsViewModel apdvm)
{
bool? filterInclude = apdvm.Filter_IncludeValue.ConvertToNullable<bool>();
var a = (from e in _repository.GetAll<Entity>()
where !filterInclude.HasValue || ea.include == filterInclude.Value
select new AggregationEntityViewModel()
{
Include = newGroup.Key.ea == null
? (true || false)
: (bool)newGroup.Key.ea.include,
}
return a;
}
just remove your (true||false) and add filterInclude == null in the where
For Nullable Value (taken from Convert string to nullable type (int, double, etc...))
public static T? ConvertToNullable<T>(this String s) where T : struct
{
try
{
return (T?)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(s);
}
catch (Exception)
{
return null;
}
}
There is an other solution:
var query = from e in _repository.GetAll<Entity>();
if (filterInclude.HasValue)
{
// when filterInclude is null (it means **ALL**),
// do not filter otherwise - check the flag
query = query.Where(entity => entity.Include == filterInclude.Value);
}
// or one-line:
// query = query.Where(entity => filterInclude == null
// || entity.Include == filterInclude.Value);
var a = query.Select(entity => new AggregationEntityViewModel { .... });
return a;
Other problem is that Convert.ToBoolean never returns null. You should create own method to parse apdvm.Filter_IncludeValue.
In order to convert to nullable type, you colud use the generic method:
public static Nullable<T> ToNullable<T>(this string s) where T: struct
{
Nullable<T> result = new Nullable<T>();
try
{
if (!string.IsNullOrEmpty(s) && s.Trim().Length > 0)
{
TypeConverter conv = TypeDescriptor.GetConverter(typeof(T));
result = (T)conv.ConvertFrom(s);
}
}
catch { }
return result;
}
Source.
Usage:
var filterInclude = apdvm.Filter_IncludeValue.ToNullable<bool>();
You can make it easier with fluent syntax like this:
private ICollection<AggregationEntityViewModel> getEntities(AggregationPracticeDetailsViewModel apdvm)
{
var query = _repository.GetAll<Entity>();
if(apdvm.Filter_IncludeValue != 'all')
{
var value = Convert.ToBoolean(apdvm.Filter_IncludeValue);
query = query.Where(q => q.include == value)
}
return query.Select(q => new AggregationEntityViewModel {...}).ToArray();
}
no need to evaluate string to nullable bool or smth. Same as no need to do strange boolean expressions.
Is there a better (nicer) way to write this if statement?
if(string1 == null && string2 == null && string3 == null && string4 == null && string5 == null && string6 == null){...}
Perhaps using the null-coalescing operator(??):
if((string1 ?? string2 ?? string3 ?? string4 ?? string5 ?? string6) == null){ ;}
If all strings are in a collection you can use Linq:
bool allNull = strings.All(s => s == null);
You could put all the strings in a list and use
if(listOfStrings.All(s=>s==null))
At the very least you can put it on multiple lines
if(string1 == null
&& string2 == null
&& string3 == null
&& string4 == null
&& string5 == null
&& string6 == null)
{...}
If you made a function like this:
public static bool AllNull(params string[] strings)
{
return strings.All(s => s == null);
}
Then you could call it like this:
if (AllNull(string1, string2, string3, string4, string5, string6))
{
// ...
}
Actually, you could change AllNull() to work with any reference type, like this:
public static bool AllNull(params object[] objects)
{
return objects.All(s => s == null);
}
string[] strs = new string[] { string1, string2, string3 };
if(strs.All(str => string.IsNullOrEmpty(str))
{
//Do Stuff
}
Or use strs.All(str => str == null) if you don't want to check for empty strings.
Make a IEnumerable of strings (list or array....), then you can use .All()
var myStrings = new List<string>{string1,string2,string3....};
if(myStrings.All(s => s == null))
{
//Do something
}
In case you want to check null or empty, here is another way without arrays:
if (string.Concat(string1, string2, string3, string4, string5).Length == 0)
{
//all null or empty!
}
Well, I don't know if it is nicer or better, or not, you can use IEnumerable.Any method like this;
Determines whether a sequence contains any elements.
List<string> list = new List<string>{"string1","string2","string3", "string4", "string5"};
if(list.Any(n => n == null))
{
}
And you can use Enumerable.All() method like;
Determines whether all elements of a sequence satisfy a condition.
if (Enumerable.All(new string[] { string1, string2, string3, string4, string5 }, s => s == null) )
{
Console.WriteLine("Null");
}
This should do the same:
if (string.IsNullOrEmpty(string1 + string2 + string3 + string4 + string5 + string6)){...}
I have the following code
SetField("TextField1", ( item.FirstName == null || item.FirstName[0] == null)
? "" : item.FirstName[0].Value);
SetField("TextField2", ( item.MiddleName == null || item.MiddleName[0] == null)
? "" : item.MiddleName[0].Value);
SetField("TextField3", ( item.LastName == null || item.LastName[0] == null)
? "" : item.LastName[0].Value);
................
like this 50-60 lines
Is there a way I can write a function and pass in parameters to reduce this code
(say for example )
void Helper(string fieldName, somethinghere )
{
SetField(fieldName,usesomethinghere);
}
We don't know the data type of the properties of item, but assuming it's T, if you define (overload):
void SetField(string fieldName, T[] itemProperty)
{
SetField(fieldName,
itemProperty == null || itemProperty[0] == null ? "" : itemProperty[0].Value);
}
then your 50-60 lines can be reduced to:
SetField("TextField1", item.FirstName);
SetField("TextField2", item.MiddleName);
SetField("TextField3", item.LastName);
...
Is that what you're looking for?
What about creating a new read-only property in the Item class?
Something like:
public String FirstName_for_display {
get {
if(FirstName == null || FirstName[0] == null)
return "";
return FirstName[0].Value;
}
}
And called your SetField with something like:
SetField("TextField1", item.FirstName_for_display)
try something like:
Private void fieldsSetter(string[] fieldnames, object[] items)
{
for(int s=0; s<fieldnames.Count(); s++)
{
SetField(fieldnames[s], (((item)items).FirstName == null || ((item)items).FirstName[0] == null) ? "" : ((item)items).FirstName[0].Value);
}
}
not tested though....