I do:
(bool success, string name1, string name2) MyFunc() {
if (DateTime.Now.Year > 2020) return (false, "", ""); // error
return (true, "some value", "some value");
}
My coding style is to try and deal with errors first, I tried:
void f() {
if (MyFunc() is (false, var name1, var name2)) return;
Console.WriteLine(name1);
}
I get
Error CS0165 Use of unassigned local variable 'name1'
in the Console.WriteLine
This works:
void f() {
if (!(MyFunc() is (true, var name1, var name2)))) return;
Console.WriteLine(name1);
}
I am trying to understand why this is so ? since MyFunc() is called, the result tuple is available, why doesn't the compiler assign it and let me use it ?
This would have been an extremely useful way of returning status+result, is there a point in requesting that feature ?
name1 and name2 will only be assigned if return value MyFunc() matches the pattern. And, in that case, the method returns.
So, Console.WriteLine(name1); will only be executed if MyFunc() doesn't match the pattern. And, in that case, name1 and name2 won't be assigned.
This will work:
void f()
{
(var error, var name1, var name2) = MyFunc();
if (error) return;
Console.WriteLine(name1);
}
C# variables definite assignment
Pattern Matching
The is type pattern expression
when clauses in case expressions
Update
The syntax in this question isn't easy to read so I got confused. Deconstruction is performed as needed, only after the a successful match. This Sharplab.io example shows that this :
void f() {
if (MyFunc() is (false, var name1, var name2)) {
Console.WriteLine(name1);
return;
}
}
Is converted to this in Release mode:
private void f()
{
ValueTuple<bool, string, string> valueTuple = MyFunc();
if (!valueTuple.Item1)
{
string item = valueTuple.Item2;
Console.WriteLine(item);
}
}
Instead of trying to check for failure, check for success and use the variables :
void f() {
if (MyFunc() is (true, var name1, var name2)) {
Console.WriteLine(name1);
}
}
Or widen the scope, by using a variable outside the block :
var (success,name1,name2)= MyFunc();
if (!success) return;
Console.WriteLine(name1);
Or use switch statements :
switch(MyFunc()) {
case (false, _,):
return ;
case (success, var name1,var name2):
Console.WriteLine(name1);
break;
}
You can also define individual variables and take advantage of the fact that assignments are expressions, but that gets ugly :
bool success;
string name1;
string name2;
if ( ((success,name1,name2)=MyFunc()) is (false,_,_)) {
return;
}
Console.WriteLine(name1);
your bool variable is not instanciated, while strings do.
try this:
static (bool success, string name1, string name2) MyFunc()
{
if (DateTime.Now.Year > 2020) return (false, "", "");
return (true, "some value", "some value");
}
static void f()
{
if (MyFunc() is var (b, name1,name2) && !b) return;
Console.WriteLine(name1);
}
public static void Main()
{
f();
}
Related
Is there a clever way to get an enum value by comparing a substring a la String.Contains with the enum?
I checked this implementation (Convert a string to an enum in C#), but would like to have an intelligent extension doing the same but including substrings comparison, something like TryParse with an option to check the whole substring of the enum values.
public static void Main()
{
string a = "HANS";
GetName(a); // prints Hans
string a1 = "Peter";
GetName(a1); // should print enum value "WolfgangPeterDietrich " containing the substring "Peter"
}
public enum MyNames
{
Hans = 0,
WolfgangPeterDietrich = 3,
}
public static void GetName(string a)
{
MyNames dif;
// this works fine, ignore case
if (Enum.TryParse(a, true, out dif))
{
System.Console.WriteLine(dif.ToString());
}
// ToDo: check for substring ??
}
This approach takes the first occurrence found, and is case insensitive.
public static void GetName(string a)
{
string? result = Enum.GetNames<MyNames>()
.FirstOrDefault(x => x.Contains(a, StringComparison.OrdinalIgnoreCase));
System.Console.WriteLine(result);
}
Just a prototype to fix with boundary cases and assertions:
public static T EnumNameContains<T>(string substringOfName) where T: struct
{
return (T)Enum.Parse(typeof(T),
Enum.GetNames(typeof(T)).FirstOrDefault(
name => name.IndexOf(substringOfName,StringComparison.CurrentCultureIgnoreCase) >= 0));
}
I guess can help.
You can try this
public static void GetName(string a)
{
MyNames dif;
// this works fine, ignore case
if (Enum.TryParse(a, true, out dif))
{
System.Console.WriteLine(dif.ToString());
}
else
{
var result = Enum.GetNames(typeof(MyNames)).Where(dd => dd.Contains(a, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
System.Console.WriteLine(result);
}
}
I have been working on refactoring some code and then i make something like this. Main idea is that i have couple Methods which contains implementations and they change referenced string variable name in my case.
What i don't like here is situation that in every if statement returning same variable (but with different result).
So my question does someone have better idea for eventually refactoring this? Is there any problem doing this in if statements (from logical, clean code side etc).
All my methods have ref keyword (for string variable name). Sorry for confusion!
private string GenerateNameFrom(IRow row)
{
string name = string.Empty;
if (Method1(name,row))
return name;
else if (Method2(name,row))
return name;
else if (Method3(name,row))
return name;
else return "Null";
}
Here is one way to do it:
private string GenerateNameFrom(IRow row)
{
var name = "";
return (Method1(ref name) || Method2(ref name) || Method3(ref name)) ? name : "Null";
}
Using var to instantiate the empty string, no need to write string twice.
Using || means that the first method that returns true will satisfy the condition and no other method will be executed.
Using the ?: operator for conditional return.
Note: As Matthew Watson commented, you are probably missing the ref (or out keyword - because even though a string is a reference type, it's also immutable, so if you want your methods to effect the content of the name argument, you must either send it as ref or as out, since the only way to change it's value is to assign a new string to it.
(also converted the String.Empty to "", but that's just personal preference)
The variable name will always have an empty string. as you declare and initialize just before if statement
Any how below the short way to get the same result. The Result will be same of the below code and your code:
if (Method1(name) || Method2(name) || Method3(name))
{
return name;
}
else
{
return "Null";
}
Or more specifically using ternary operator
(Method1(name) || Method2(name) || Method3(name)) ? name : "Null";
Can't see the implementation of your methods but I'm assuming something like:
public bool Method1(ref string name)
{
if (condition)
{
name = "SomeValue";
return true;
}
return false;
}
You could refactor those methods to return the updated name:
public string Method1(name)
{
if(condition)
{
return "SomeValue";
}
return null;
}
And then you could just null coalesce the method calls:
private string GenerateNameFrom(IRow row)
{
string name = string.Empty;
return Method1(name)
?? Method2(name)
?? Method3(name)
?? "Null";
}
Well all of this Method1(name) and Method2(name) doing some validation over the input string name and returning bool. In that case would suggest you to combine all those validation logic inside single method using a switch statement probably and use that method instead
I would try to avoid using ref in this case. Instead, you could make the various methods return a tuple (bool success, string value) like so:
public static (bool success, string value) Method1(string name)
{
if (name == "test")
return (true, "changed");
return (false, null);
}
public static (bool success, string value) Method2(string name)
{
if (name == "test")
return (true, "changed");
return (false, null);
}
public static (bool success, string value) Method3(string name)
{
if (name == "test")
return (true, "changed");
return (false, null);
}
Then you can write the calling code like so (it's not shorter, but it avoids ref). Whether you like this better is probably a matter of taste...
private string GenerateNameFrom(/*IRow row*/)
{
string name = string.Empty;
var result = Method1(name);
if (result.success)
return result.value;
result = Method2(name);
if (result.success)
return result.value;
result = Method3(name);
if (result.success)
return result.value;
return null;
}
Alternatively, if null can be used to indicate "no result" then just do a similar thing but checking the return value for null:
private string GenerateNameFrom(/*IRow row*/)
{
string name = string.Empty;
var result = Method1(name);
if (result != null)
return result;
result = Method2(name);
if (result != null)
return result;
return Method3(name);
}
public static string Method1(string name)
{
if (name == "test")
return "changed";
return null;
}
public static string Method2(string name)
{
if (name == "test")
return "changed";
return null;
}
public static string Method3(string name)
{
if (name == "test")
return "changed";
return null;
}
So we have ternary operators. Great! Then there's the ?? operator, which does a coalesce over a nullable variable.
Example:
string emptyIfNull = strValue ?? "";
Question: Is it possible to implement a simple operator like this for a try-catch?
Example:
string result = CoalesceException(someExpression, "");
public static T CoalesceException<T>(expression, defaultValue)
{
try
{
return evaluate expression; // ?
}
catch
{
return defaultValue;
}
}
Is it possible to implement a method that can be used as easily as possible, or even some kind of coalesce-like operator?
You can:
public static T CoalesceException<T>(Func<T> func, T defaultValue = default(T))
{
try
{
return func();
}
catch
{
return defaultValue;
}
}
but I'm not sure this is what you want...
use:
string emptyIfError = CoalesceException(() => someExpressionThatReturnsAString, "");
for example...
string shortString = null;
string emptyIfError = CoalesceException(() => shortString.Substring(10), "");
will return "" instead of NullReferenceException
important
The function, as written, will cause the "evaluation" of defaultValue always. Meaning:
string Throws() { throw new Exception(); }
string str1 = somethingTrue == true ? "Foo" : Throws();
Here an exception won't be thrown, because Throws() won't be evalued. The same happens with the ?? operator.
string str2 = CoalesceException(() => ((string)null).ToString(), Throws());
This will cause an exception before entering in CoalesceException. Solution:
public static T CoalesceException<T>(Func<T> func, Func<T> defaultValue = null)
{
try
{
return func();
}
catch
{
return defaultValue != null ? defaultValue() : default(T);
}
}
Use:
string emptyIfError = CoalesceException(() => someExpressionThatReturnsAString, () => "");
Here a little something that I've end up, to create a One Liner TryCatch
Usage
var r = Task.TryCatch(() => _logic.Method01(param1, param2));
TryCatch definition
public static class Task
{
public static TResult TryCatch<TResult>(Func<TResult> methodDelegate)
{
try
{
return methodDelegate();
}
catch (Exception ex)
{
// .. exception handling ...
}
return default(TResult);
}
}
Within C# is it possible to create a new function on the fly to define a variable?
I know that
string getResult() {
if (a)
return "a";
return "b";
}
String result = getResult();
is possible, but I'm looking for something like
String result = new string getResult() {
if (a)
return "a";
return "b";
}
Is this possible? If so, would someone demonstrate?
EDIT
It is possible
Edit: Final - Solution
This is the end result of what I barbarically hacked together
Func<string> getResult = () =>
{
switch (SC.Status)
{
case ServiceControllerStatus.Running:
return "Running";
case ServiceControllerStatus.Stopped:
return "Stopped";
case ServiceControllerStatus.Paused:
return "Paused";
case ServiceControllerStatus.StopPending:
return "Stopping";
case ServiceControllerStatus.StartPending:
return "Starting";
default:
return "Status Changing";
}
};
TrayIcon.Text = "Service Status - " + getResult();
One way to define such a function:
Func<bool, string> getResult = ( a ) => {
if (a)
return "a";
return "b";
}
You can then invoke: string foo = getResult( true );. As a delegate, it can be stored/passed and invoked when needed.
Example:
string Foo( Func<bool, string> resultGetter ){
return resultGetter( false );
}
You can also close around variables within scope:
bool a = true;
Func<string> getResult = () => {
if (a)
return "a";
return "b";
}
string result = getResult();
You want to use the inline if statement.
string result = a ? "a" : "b";
If you really want inline you can make an extension method for type String:
static class StringExtensions {
public static string ExecuteFunc(
this string str,
Func<string, string> func
) {
return func(str);
}
}
And then, when you want to use it, you do so like so:
string str = "foo";
string result = str.ExecuteFunc( s => {
switch(s){
case "a":
return "A";
default:
return "B";
}
}
);
Not sure what the scope of the variable a is in your example, but assuming that it is accessible within the scope (as it is in your example):
Func<string> getResult = () =>
{
return a ? "a" : "b";
}
Based on the solution you posted, here is a better way to do this.
Change your ServiceControllerStatus enum to add [Description] attributes:
public enum ServiceControllerStatus
{
[Description("Running")]
Running,
// ... others
}
Add the following extension method in a new static class:
public static string ToDescription(this Enum value)
{
var attributes = (DescriptionAttribute[])value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : value.ToString();
}
Now you can simply write:
TrayIcon.Text = "Service Status - " SC.Status.ToDescription();
The following generic static method takes a string and returns an enum.
It nicely ignores case since I set the ignoreCase parameter to true.
However, I also want to test if the enum exists, but the enum.IsDefined method to do this doesn't seem to have an ignoreCase parameter.
How can I test if the enum is defined or not and at the same ignore case?
using System;
namespace TestEnum2934234
{
class Program
{
static void Main(string[] args)
{
LessonStatus lessonStatus = StringHelpers.ConvertStringToEnum<LessonStatus>("prepared");
ReportStatus reportStatus = StringHelpers.ConvertStringToEnum<ReportStatus>("finished");
Console.WriteLine(lessonStatus.ToString());
Console.WriteLine(reportStatus.ToString());
Console.ReadLine();
}
}
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
if (Enum.IsDefined(typeof(T), text)) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
}
}
public enum LessonStatus
{
Defined,
Prepared,
Practiced,
Recorded
}
public enum ReportStatus
{
Draft,
Revising,
Finished
}
}
public enum MyEnum
{
Bar,
Foo
}
class Program
{
static void Main(string[] args)
{
var containsFoo = Enum.GetNames(typeof(MyEnum)).Any(x => x.ToLower() == "foo");
Console.WriteLine(containsFoo);
}
}
Along with #Darin's answer, in .NET 4.0, the Enum type now has a TryParse method:
MyEnum result;
Enum.TryParse("bar", true, out result);
The important thing to remember is that there is a fundamental difference in the behaviour of Parse vs TryParse. Parse methods will throw exceptions. TryParse methods will not. This is quite important to know if you are potentially trying to parse many items.
You might be able to get away with simply using Enum.TryParse, as others have said.
However, if you want a more robust/general conversion that allows you to convert more than just strings, then you need to also use Enum.IsDefined, which unfortunately, as you found, is not case-insensitive.
Enum.TryParse is (can be) case-insensitive. But unfortunately, it allows out-of-range ints to get through!
So the solution is to use them together (and the order is important).
I wrote an extension method that does just that. It allows conversion from string, int/int?, and any other Enum/Enum? type like so:
string value1 = "Value1";
Enum2 enum2 = value1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == value1);
Enum1 enum1 = Enum1.Value1;
enum2 = enum1.ParseToEnum<Enum2>();
Debug.Assert(enum2.ToString() == enum1.ToString());
int value2 = 1;
enum2 = value2.ParseToEnum<Enum2>();
Debug.Assert(enum2.GetHashCode() == value2);
Here's the heart of the method. This is the conversion part that answers your question. The variable value is of type object because of the "overloads" I have that take different types as the main input (see above), but you can do this with a variable of type string just fine if that's all you want (obviously changing value.ToString() to just value).
if (value != null)
{
TEnum result;
if (Enum.TryParse(value.ToString(), true, out result))
{
// since an out-of-range int can be cast to TEnum, double-check that result is valid
if (Enum.IsDefined(typeof(TEnum), result.ToString()))
{
return result;
}
}
}
There's a lot more to my extension method... it allows you to specify defaults, handles out-of-range ints just fine, and is fully case-insensitive. I can post more of it if anybody's interested.
Use Enum.TryParse instead:
T val;
if(Enum.TryParse(text, true, out val))
return val;
else
return default(T);
I'm using Compact Framework 3.5, and:
Enum.TryParse
...doesn't exist. It does have:
Enum.IsDefined
..but that doesn't support the ignoreCase parameter.
I'd like the best of both worlds, so came up with this (as a helper method)...
public bool TryParse<TEnum>(string value, bool ignoreCase, ref TEnum result) where TEnum : struct
{
bool parsed;
try
{
result = (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
parsed = true;
}
catch { }
return parsed;
}
HTH
enum DaysCollection
{
sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
public bool isDefined(string[] arr,object obj)
{
bool result=false;
foreach (string enu in arr)
{
result = string.Compare(enu, obj.ToString(), true) == 0;
if (result)
break;
}
return result;
}
private void button1_Click(object sender, EventArgs e)
{
object obj = "wednesday";
string[] arr = Enum.GetNames(typeof(DaysCollection)).ToArray();
isDefined(arr,obj);
}
Make text same case as enum string:
enum FileExts
{
jpg,
pdf
}
if (Enum.IsDefined(typeof(T), text.tolower())) //does not have ignoreCase parameter
return (T)Enum.Parse(typeof(T), text, true);
else
return default(T);
I had a similar concern and used a combination of both the .Enum.TryPase (with the case-insensitive flag set as true) and Enum.IsDefined. Consider the following as a simplification to your helper class:
public static class StringHelpers
{
public static T ConvertStringToEnum<T>(string text)
{
T result;
return Enum.TryParse(text, true, out result)
&& Enum.IsDefined(result.ToString())
? result
: default(T);
}
}
And while we're at it, since the helper class is static and the method is static - we could make this an extension method on string.
public static class StringExtensions
{
public static TEnum ToEnum<TEnum>(this string text)
where TEnum : struct, IComparable, IFormattable, IConvertible
{
TEnum result = default(TEnum);
return !string.IsNullOrWhiteSpace(text)
&& Enum.TryParse(text, true, out result)
&& Enum.IsDefined(typeof(TEnum), result.ToString())
? result
: default(TEnum);
}
}
Here, I created a .NET Fiddle that clearly demonstrates this.
First use Enum.TryParse method to get an object of type T, then pass that object to Enum.IsDefined method:
private static T ConvertStringToEnum<T>(string stringValue) where T : struct
{
if (System.Enum.TryParse(stringValue, out T result))
{
if (System.Enum.IsDefined(typeof(T), result) || result.ToString().Contains(","))
return result;
throw new System.Exception($"{stringValue} is not an underlying value of the {typeof(T).FullName} enumeration.");
}
throw new System.Exception($"{stringValue} is not a member of the {typeof(T).FullName} enumeration.");
}
public static T ConvertStringToEnum<T>(string text)
{
T returnVal;
try
{
returnVal = (T) Enum.Parse( typeof(T), text, true );
}
catch( ArgumentException )
{
returnVal = default(T);
}
return returnVal;
}