How to return the method? - c#

I need to split it to another function and return see whether it contains some value or not but I'm not so sure how to make it. Example
exList = getList(ref Path, type);
if(exList.Count > 0){
Do something...
}
Im not so sure this part how to write... this is my half work
static object getList(ref string Path, string type)
{
exList = new List<Email>();
string[] jsonFileList = Directory.GetFiles(Path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
exList.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
File.Delete(file);
}
}
}
return something;
}

check below code
static List<Email> getList(ref string Path, string type)
{
exceptionList = new List<Email>();
string[] jsonFileList = Directory.GetFiles(Path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
List.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
File.Delete(file);
}
}
}
return exceptionList;
}

Your function needs to return a list and you need to save the return value into a variable.
// not List = ..., List is a class, you need a new instance of a list.
List<Email> list = getList(path, type);
if (list.Count > 0)
{
// Do Something
}
// [...]
private List<Email> getList(string path, string type)
{
List<Email> ret = new List<Email>();
string[] jsonFileList = Directory.GetFiles(path, type + "_*.json");
if (jsonFileList.Length > 0)
{
//read json file
foreach (string file in jsonFileList)
{
if (File.Exists(file))
{
// not List.Add(), List is a class, you need to add to the instance of a list.
ret.Add(JsonConvert.DeserializeObject<ExceptionEmail>(File.ReadAllText(file)));
// File.Delete(file); // The method shouldn't delete files when it's name is getList, delete them after handling in the calling method.
}
}
}
return ret;
}
Also you should work on your style.
Please do use strong types wherever possible. (i.E. no Object)
Try to avoid static functions and variables unless you need them.
For readability write the access modifier.
Variable and argument names should be lower case and constants capitals. Only classes, enums and interfaces, structs, etc. should start with a capital letter.
Only use reference arguments if you need to. Call by value is default for a reason. (The reason being encapsulation, and avoiding side effects. Also you would expect the function here to alter path if you say it's byref, which it doesn't.)
getList shouldn't delete files. You wouldn't expect that from a name like that. Delete the files AFTER you processed them in the loop in the calling method.
please review the difference between classes and objects/instances.
please review function calls and return values.

Related

Trying to map "similar looking" tuples returned from multiple functions to one variable

Say there is a library with code of this form:
public class SomeLib
{
public (bool WasOk, string Message) DoCheck(string fileName)
{
// do something here to check the file.
return (true, "all good");
}
public (bool WasOk, string Message) DoCheck(byte[] fileBytes)
{
// do something here to check the file as bytes
return (true, "all good");
}
}
The code which uses this library looks of this form:
int option = 1;
SomeLib lib = new SomeLib();
if (option == 0)
{
var res1 = lib.DoCheck("hello");
MessageBox.Show(res1.WasOk + res1.Message);
}
else
{
var res2 = lib.DoCheck(new byte[] { });
MessageBox.Show(res2.WasOk + res2.Message);
}
What I was hoping to do, was to store the returned value from the two calls to DoCheck, to a common variable. e.g.:
int option = 1;
SomeLib lib = new SomeLib();
var res; // doesn't compile, obviously
if (option == 0)
{
res = lib.DoCheck("hello");
}
else
{
res = lib.DoCheck(new byte[] { });
}
MessageBox.Show(res.WasOk + res.Message); // act on the result in the same way for both branches.
I hope this makes sense. Is what I am trying to do possible? Let's say that the code in the library cannot be changed.
var res doesn't compile because it doesn't have any type information. If you're making a statement that declares a variable the compiler has to be able to work out the type somehow, either from the left or the right side:
StringBuilder sb = new StringBuilder(); //both sides, available from the dawn of time
var sb = new StringBuilder(); //for a long time
StringBuilder sb = new(); //more recently
If there isn't a right side then the left side has to have the type info:
StringBuilder sb;
Thus for your case you need to put the type info on the left. You can even rename the members (but you don't have to)
(bool AllGood, string Note) res;
(bool WasOk, string Message) res;
(bool, string) res; //access the bool in res.Item1, string in res.Item2
You could do it in a one with a ternary:
var res = (option == 0) ? lib.DoCheck(hello) : lib.DoCheck(new byte[] { });
Both your methods return tuples with the same named members so your res will have res.WasOk and res.Message so it's compiled as if it's this:
(bool WasOk, string Message) res = ...
If your tuples coming out your methods had different names it would still compile as if it was this:
(bool, string) res = ...
And you could still access the data via Item1 and Item2
If you're doing this ternary approach then you could deconstruct the tuple too, which also allows you to rename:
var(allGood, note) = ... ? ... : ... ;
MessageBox.Show(note);
Instead of one tuple variable where you say res.Whatever, this has created two variables like as if you'd done this:
var res = ... ? ... : ... ;
var allGood = res.WasOk;
var note = res.Message;
Plenty of options for dealing with tuples..
Implicitly typed locals must be initialized
An implicitly typed local variable must be initialized with a value at the same time that it is declared. here
To correct this error
Assign a value to the variable or else give it an explicit type.
(bool AllGood, string Note) res;

Parsing command line arguments of different formats

I'm trying to convert command line arguments to different formats. For example, if I'm trying to get a specific user, it would be GetUser=UserName, but I also have methods that don't need the equals sign, such as GetAllUsers. Right now I'm currently splitting on the = but I don't need to do that for all commands. I tried setting it to a conditional, where if = is detected, it will split, otherwise it will just take the argument as a string, but I'm getting Cannot implicitly convert type 'string' to 'System.Collections.Generic.Dictionary<string, string>
Code:
public static Dictionary<string, string> ParseArgs(string[] args)
{
Dictionary<string, string> results = new Dictionary<string, string>();
foreach (string arg in args)
{
string[] parts = arg.Split('=');
if (parts.Length > 1)
{
results[parts[0]] = parts[1];
continue;
}
else
{
results = Convert.ToString(arg);
}
}
return results;
}
You're trying to assign a string to a dictionary object. If you want a collection (like Dictionary) then you should use the Add method like so:
foreach (string arg in args)
{
string[] parts = arg.Split('=');
if (parts.Length > 1)
{
//results[parts[0]] = parts[1];
results.Add(parts[0], parts[1]);
continue;
}
else
{
results.Add("GetUser", arg);
}
}
I've forced it to be "GetUser" as the value here, but you may actually want something different. Your dictionary is using Key-Value pairs and you would say results["GetUser"] to return a string for "GetUser". You could also use TryGetValue to validate that GetUser was actually provided.
Additionally, if you just want to return the string that's after GetUser (e.g. Username), then you could change results to a string and just assign that directly and skip the whole use of a Dictionary, which seems overkill for your sample (perhaps your real project is much more complex though).
In the else part, you are trying to assign string to dictionary via the statement results = Convert.ToString(arg);
So, please change else part with something like as shown below:
public static Dictionary<string, string> ParseArgs(string[] args)
{
Dictionary<string, string> results = new Dictionary<string, string>();
foreach (string arg in args)
{
string[] parts = arg.Split('=');
if (parts.Length > 1)
{
results[parts[0]] = parts[1];
// continue; no need to use continue, as there are no more statements after this if-else, loop will anyway continue. but please uncomment if you have any in your actual code.
}
else
{
results[arg] = arg; // assuming arg string in args won't repeat, meaning its unique. Otherwise please use ContaineKey if condition before adding or over-writing.
}
}
return results;
}

How to create FUNC Action List with Parameters and Return Values

I am trying to create my first list of actions that I will be able to check a status within the passed object.
But I can't get it to work - its giving me an error on the return type. But if I change the return type to what it wants - then I can't pass values down.
Sample code:
public class Class1
{
public Main()
{
var decisionObject = new DecisionObject();
var decisionList = new List<Func<DecisionObject, DecisionObject>>
{
Method1(decisionObject),
Method2(decisionObject),
Method3(decisionObject)
};
var exitLoop = false;
foreach (var method in decisionList)
{
decisionObject = method(decisionObject);
switch (decisionObject.Status)
{
case Status1:
exitLoop = true;
break;
case Status2:
case Status3:
case Status4:
break;
}
if (exitLoop) break;
}
}
public Func<DecisionObject, DecisionObject> Method1(DecisionObject
decisionObject)
{
decisionObject = SomeOtherMethod(decisionObject);
return decisionObject;
}
}
What am I missing here?
If I'm not mistaken, Method1,Method2, and Method3 are simply supposed to accept a decision object and return a different one. So they would be defined like this (hopefully this is straightforward to you):
DecisionObject Method1(DecisionObject input)
{
var output = SomeMethod(input);
return output;
}
You then want to put all these methods into a list and execute them. To put them into a list, put the method names in the code without parentheses. That tells C# that you want a reference to the method itself, rather than the result of invoking the method.
var decisionList = new List<Func<DecisionObject, DecisionObject>>
{
Method1, //Do not invoke-- just store a reference to the method
Method2,
Method3
};
You can then invoke them by passing the decision object in:
foreach (var func in decisionList)
{
var result = func(decisionObject);
}
The key thing here to remember is that when you put parentheses after a symbol, it tells C# to invoke it. So don't put parentheses if all you want is a reference to the method itself.
decisionObject = SomeOtherMethod(decisionObject)
Isn't probably returning a func but a value.
You could do this:
public Func<DecisionObject, DecisionObject> Method1()
{
var myFunc = (myObject) => SomeOtherMethod(myObject);
return myFunc;
}
That will create and returns a new func that expects one parameter and invokes SomeOtherMethod.
Please note that the parameter of Method1 isn't needed in this approach and so I removed it.

C# Find Properties of Classes in a .dll

I'm trying to create a method that counts the properties of a given class.
I want to pass in the class name as a string perhaps and then can turn the string into a reference of the given class. I have literally hundreds of classes (generated by Thrift) that could be passed in, so its not practical to give each class its own property counter.
My purpose is to provide arguments to a class that dynamically creates a UI based on what will need to be input by the user for each specific method and what will be returned. To save myself from having to manually write a UI for every method.
Is there a good way to do this?
Here's what I have so far.
class PropertyCounter
{
public int PropertyCounter(string nameOfClass)
{
int count = typeof(nameOfClass).GetProperties().Count();
return count
}
}
I got this working... using Assembly. Took some doing but it does what i need it to do.
Now, I was thinking of making these into a list of 'class' objects, but I'm thinking a string would work just as well for an argument.
Thanks to all who offered assistance.
class Discover
{
public void DiscoverProperties()
{
var me = Assembly.GetExecutingAssembly().Location;
var dir = Path.GetDirectoryName(me);
var theClasses = dir + #"dllName.dll";
var assembly = Assembly.LoadFrom(theClasses);
var types = assembly.ExportedTypes.ToList();
int propCount;
string propertiesList;
string cName;
string tempString;
foreach (var t in types)
{
propertiesList = "";
propCount = 0;
cName = t.Name;
foreach (var prop in t.GetProperties())
{
propCount++;
tempString = $"{prop.Name}:{prop.PropertyType.Name} ";
propertiesList = propertiesList += tempString;
}
}
}
}
You could use Activator.CreateInstance, with the overload which accepts two strings: one for the assembly in which the type is located, and one which specifies the type (in your case, class).
https://msdn.microsoft.com/en-us/library/d133hta4(v=vs.110).aspx
public int PropertyCounter(string nameOfClass) {
return Activator.CreateInstance(nameOfAssembly,
nameOfClass).GetType().GetProperties().Count();
}
You should check for failure

Function that returns different types?

Suppose I have a container class that contains two properties:
string contents
bool isCommaSeperated
And a function:
? compute()
{
if(this.isCommaSeperated)
{
return contents.split(",").toList();
}
else
{
return contents;
}
}
Is there a way for this function to return either a string or a list of strings?
Or what type of design would allow me to achieve something similar?
I would just return both results as a collection:
IList<string> compute()
{
if (this.isCommaSeperated)
{
return contents.Split(","); // No need to turn the array into a list
}
return new[] { contents };
}
You can use dynamic to implement that:
dynamic Compute(bool isCommaSeperated)
{
if(isCommaSeperated)
{
return contents.Split(",").ToList();
}
else
{
return contents;
}
}
Code will still preserve type information, but let you return any type you like and fail at run-time if you try to use methods of another type.
Note that you give up compile type safety by doing it. Depending on your needs it may be ok, but consider is some alternative solution that preserve compile type safety would work better. I.e. returning single element array as shown in istme86's asnwer.
While IEnumerable<string> is the standard solution, you could also try the new StringValues struct which implicitly casts between string values and string arrays:
StringValues a = "A";
StringValues b = new string[] { "A", "B" };
StringValues c = null;
Console.WriteLine(a.Contains("A"));
// true
Console.WriteLine(b.Any(x => x == a));
// true
Console.WriteLine(c.Any());
// false

Categories