I have some methods that take 20 or more strings as parameters. I was wondering what works better: passing 20 string parameters to the method or putting them all in a dictionary and passing it as only parameter.
Multiple strings:
public Boolean isNice(string aux, string aux2, string aux3, string aux4, string aux5, string aux6, string aux7,
string aux8, string aux9, string aux10, string aux11, string aux12, string aux13, string aux14, string aux15, string aux16)
{
string foo1 = aux;
string foo2 = aux2;
// etc
return true;
}
public void yeah()
{
string aux = "whatever";
string aux2 = "whatever2";
// etc
isNice(aux, aux2, ..., ..., ...);
}
Dictionary of strings
public Boolean isNice(Dictionary<string, string> aux)
{
string foo1 = aux["aux1"];
string foo2 = aux["aux2"];
// etc
return true;
}
public void yeah()
{
string aux = "whatever";
string aux2 = "whatever2";
// etc
Dictionary<string, string> auxDict = new Dictionary<string,string>();
auxDict.Add("key1", aux);
auxDict.Add("key2", aux2);
// etc
isNice(auxDict);
}
My question is regarding performance, readability and code simplicity.
Right now I'm using multiple strings: should I use dictionaries instead?
This depends. Are all 20 parameters required for the function to work?
If so, create a data type that can communicate all 20 values and pass in an instance of that data type. You could create helper classes to easily initialize that type of object. You can easily pass in a new instance of that data type, and provide flexible ways to initialize the type:
isNice(new niceParams
{
aux1 = "Foo",
aux2 = "Bar"
// ...
}
);
If not, put the optional parameters at the end of the signature, and give them default values.
public Boolean isNice(string req1, string req2, string optional1 = null)
This way, you have overloads to specify exactly which values you want to provide.
Another benefit of this is you can used named parameters to call the function:
isNice(req1, req2, optional1: "Foo", optional15: "Bar");
With that said, I would not use a dictionary. It forces the caller to understand the signature, and completely breaks any compiler type safely. What if required values aren't provided? What if a key is misspelled? All this checking has to now be done at runtime, causing errors that can only be caught at runtime. To me, it seems to be asking for trouble.
The main difference is that in case when you have 20 string parameters the compiler will ensure that all of them are explicitly set, even if they are set to null. In case of passing a collection the compiler will not be able to detect that somebody has forgotten to set the aux17 parameter: the code that uses a dictionary-based API would continue to compile, so you would be forced to add an extra check at run-time.
If it is OK with your code to not have a compiler check, for example, because all your string values are optional, then a collection-based approach is easier to maintain.
The difference in speed cannot be predicted until you implement the change. A collection-based approach would perform an additional memory allocation, so it would consume more CPU cycles. On the other hand, the difference may be too small to have a real impact on the overall performance of your program.
Note that since your parameters are named uniformly, it appears that they may be placed in a "flat" collection, rather than a dictionary. For example, you could make an API taking a list or an array of strings. In case of an array, you could also make your method take variable number of parameters, so that the callers could use the old syntax to call your method:
public bool isNice(params string[] args)
Related
I have In Rule method that should accept set of simple types e.g. array of string type or array of integers (int16).
I tried to create following methods
/* Method with string array*/
[Method(DisplayName = "[M1]")]
public int Method1([Parameter(ValueInputType.All)]
string[] valStrings )
{
return valStrings.Length; // any calculation goes here
}
}
/* Method with PARAMS string array*/
[Method(DisplayName = "[M2]")]
public int Method2([Parameter(ValueInputType.All)]
params string[] valStrings )
{
return valStrings.Length; // any calculation goes here
}
}
/* Method with INT ARRAY referencing on external data source*/
[Method(DisplayName = "[M2]")]
public int Method2([Parameter(ValueInputType.All,
DataSourceName="myDataList" )]
params int[] valInt )
{
return valInt; // any calculation goes here
}
}
It seems that codeeffect library 4.3.2.6 does not support passing user entered parameters into the In-Rule method and restricts In-Rule method with only simple parameter type e.g. Int but not int[] . Seems that I cannot connect DataSource with In Rule method to pass more than one item from data source into the in-rule method. The main idea that only USER should enter either the simple type(s) or select more than one data source item? I cannot restrict user to pass Source Object Field from source object that returns collection.
Any suggestions or workarounds?
CodeEffects supports in-rule methods that take fields of IEnumerable types as params. In-rule methods can also return IEnumerable collections, including arrays. It cannot take arrays as params, though, due to certain limitations in .NET reflection. Code Effects also doesn't support user input of any kind of arrays in general simply because that would be a UI nightmare for devs and end users. More info is available here and here
You can use Dynamic Menu Data Sources to bring data from your database to be used as values or as params. Details can be found here
I came across an example similar to this:
public Dictionary<string, object> generate(
string elementId,
Dictionary<string, object> additionalAttributes = null)
{
.... method body
}
Why would the dictionary passed as parameter be initiated to null? I haven't seen such construct. Does it have to do something with an optional parameter?
I can't speak to your first question, but the answer to your second question is yes. This is an optional parameter. C# only allows optional reference-type parameters to take a default value of null, except for string, which can take any constant string value, I believe.
Ref: MSDN
I use that to save time writing functions overloading. For example, instead of overloading two functions:
void SameFunctionName(Parameter1){ .. }
void SameFunctionName(Parameter1, Parameter2){ .. }
// maybe additional function body with three parameters .. etc
I just write one using this case:
void MyFunction(Parameter1, Parameter2 = null){ .. }
So, a small if statement inside my function would check if Parameter2 is null or not, to then make decisions. All in one function body.
and the function call for this case would work in both cases:
MyFunction(Parameter1); // This is a valid syntax
MyFunction(Parameter1, Parameter2); // This is a valid syntax
Optional parameter such as the one you use in your example can only be set to constant values, this means that you can't use any reference values, which is what Dictionary is, as such null is the only allowed value you can initialise an optional variable of type Dictionary to, if the method was using a value type like int or string then a value could be initialised for the optional parameter otherwise it has to be null
Yes it saves time if you are using Function Overloading For example this
can be avoided
Void Main()
{
fun1(11);
fun1(12,13);
}
public fun1(int i )
{
Print(i);
}
public fun1(int i ,int j)
{
Print(i+j);
}
This can be avoided by Code below and it also saves time and space
Void Main()
{
fun1(12);
}
public fun1(int i ,int j = NULL)
{
if(j==NULL)
Print(i);
else
Print(i+j);
}
I need to read a tab (or comma) separated text and assign them to a list of built-in variables (int, char, string, etc. but not object). I want to handle this with a generic method. Here is the working code I am using for the case with three variables.
public static void ReadVariablesFromString<T0, T1, T2>(out T0 input0, out T1 input1, out T2 input2, string stringIn, char[] separators)
{
string[] stringParts = stringIn.Split(separators);
input0 = ConvertTo<T0>(stringParts[0]);
input1 = ConvertTo<T1>(stringParts[1]);
input2 = ConvertTo<T2>(stringParts[2]);
}
public static T ConvertTo<T>(object value)
{
return (T)Convert.ChangeType(value, typeof(T));
}
When I want to assign the tab separated text to three variables of different type, this is what I use:
string str = "a\t123\tbcd";
char var1;
int var2;
string var3;
char[] separators = { '\t' };
ReadVariablesFromString(out var1, out var2, out var3, str, separators);
Is there a way to generalize this model for different number of variables? Is it possible to write a generic method that accepts different number of variables for this specific case?
One of the other solution might be writing this method for a variable list with different types. Instead of passing variables one by one, is there a way to pass them in a list?
No, in C# you can not send array of variable references.
But what you can do is create a class with some properties, and then use reflection to fill them.
See Enumerating through an object's properties (string) in C# and Set object property using reflection
and How to dynamically cast an object of type string to an object of type T on some details.
I suggest you return a List
List<object> variables = ReadVariablesFromString(str, separators);
Where each entry in the list will be the values your parse from the input string, for example
variables.ElementAt(0) will be 'a'
Since the type of the list is 'object', you can store any data type in there, since each type ultimately inherits from object anyway. You would need to cast the data when you need to use it though
char letter = (char)variables.ElementAt(0)
But you must be doing something similar already, otherwise how would you know which variable to put in each incoming out parameter.
Doing this means you have more flexibility in the amount of variables you can return from ReadVariablesFromString()
I've got a data layer I'm working on that calls into a database three times with three different stored procedures. I initially created three different functions to retrieve three dimensionalities of results. The first returns a single value, the second an entire row, and the third a table. They also take in different parameters. The first takes two varchars, the second two ints, and the last three varchars. If I try to get fancy and merge it all down as shown below, am I going to have problems?
public void CallStoredProcedure(string[] astrParams, string strConnectionString, string strStoredProcedure)
{
int nParams = 0;
SqlParameter[] asqlParams;
asqlParams = SqlHelperParameterCache.GetSpParameterSet(strConnectionString, strStoredProcedure);
foreach (string strParam in astrParams)
{
asqlParams[nParams].Value = strParam;
nParams++;
}
}
Alternately, can I use an array of mixed data types without know what is in there, and can I assign different types into the same array, replacing elements?
object[] aParams;
string strName = "Joe";
long lngHeight = 180;
object[0] = strName;
object[1] = lngHeight;
CallStoredProcedure(aParams, strConnectionString, "StoredProcedure1")
long lngWeight = 3;
string strItemName = "Bookend";
object[0] = lngWeight;
object[1] = strItemName;
CallStoredProcedure(aParams, strConnectionString, "StoredProcedure2")
And then change the code inside that function to:
foreach (object oParam in astrParams)
{
asqlParams[nParams].Value = oParam;
nParams++;
}
Would I need to use ToString() in either or both of these cases? And if so, does that essentially turn them into the same thing? Like I said, right now I've got three functions that take in all the parameters correctly typed, but I'm trying to get rid of duplicate code if possible.
You cannot have an array of different data types, you can have an array of objects and cast the individual elements to a specific data type when saving the values to typed variable though.
Not sure where you want to use ToString() but if its when you are saving a string into an Object array there is no point but if you are saving an object to a string variable then yes you would need to do this:
string str = objectArray[0].ToString();
I would avoid this whole mess though, and follow what DJ KRAZE said and add your parameters via Parameter.AddWithValue though.
I was fairly confused about what I needed to do with my initial question. What I realized is that I don't need to worry about C# types when adding the parameters, since GetSpParameterSet pulls all the information I need into the SqlParameter array, and resets the array every time I call it. So my initial code:
foreach (string strParam in astrParams)
{
asqlParams[nParams].Value = strParam;
nParams++;
}
...will just work for my needs. There won't be any problem assigning a C# string type into an SQL int type, which was my main concern, since the languages don't have identical types anyway. I know that I won't have to worry about anything like an "x" going into an int type because all the values I'm using are being pulled from the properly typed columns in other SQL queries already. So I won't need any object arrays.
I guess this could also be asked as to how long the created type name is attached to an anonymous type. Here's the issue:
A blog had something like this:
var anonymousMagic = new {test.UserName};
lblShowText.Text = lblShowText
.Text
.Format("{UserName}", test);
As sort of a wish list and a couple ways to go at it. Being bored and adventurous I took to creating an string extension method that could handle this:
var anonymousMagic = new {test.UserName, test.UserID};
lblShowText.Text = "{UserName} is user number {UserID}"
.FormatAdvanced(anonymousMagic);
With the idea that I would get the property info from the anonymous type and match that to the bracketted strings. Now with property info comes reflection, so I would want to save the property info the first time the type came through so that I wouldn't have to get it again. So I did something like this:
public static String FormatAdvanced(this String stringToFormat, Object source)
{
Dictionary<String, PropertyInfo> info;
Type test;
String typeName;
//
currentType = source.GetType();
typeName = currentType.Name;
//
//info list is a static list for the class holding this method
if (infoList == null)
{
infoList = new Dictionary<String, Dictionary<String, PropertyInfo>>();
}
//
if (infoList.ContainsKey(typeName))
{
info = infoList[typeName];
}
else
{
info = test.GetProperties()
.ToDictionary(item => item.Name);
infoList.Add(typeName, info);
}
//
foreach (var propertyInfoPair in info)
{
String currentKey;
String replacement;
replacement = propertyInfoPair.Value.GetValue(source, null).ToString();
currentKey = propertyInfoPair.Key;
if (stringToFormat.Contains("{" + currentKey + "}"))
{
stringToFormat = stringToFormat
.Replace("{" + currentKey + "}", replacement);
}
}
//
return stringToFormat;
}
Now in testing, it seems to keep the name it created for the anonymous type so that the second time through it doesn't get the property info off the type but off the dictionary.
If multiple people are hitting this method at the same time, is it pretty much going to work in a Session like fassion; IE the names of the types will be specific to each instance of the program? Or would it be even worse than that? At what point does that name get chucked and overwritten?
It never does. The type is generated at compile-time and you can consider it constant and unique throughout the life of the app-domain.
I question the value of this function though. The obvious first reason is because you don't have much of the functionality of the Format method on the String class (no escape for brackets, no formatting of values in the brackets, etc, etc).
The second is that it basically links the format string to the type being passed in, so they are not swapped out easily. If I had two classes which had the same conceptual value, but different properties naming it, I have to change my format string to display it with your method to compensate for the fact that the property name is embedded in the format string.
Anonymous types are generated at compile time, and so the reflection names should be static as long as you don't re-compile the assembly.
There is a detailed post here that describes the names, but I believe what you are doing is safe.
I have two things to say, but this isn't really an answer.
First of all, the Dictionary doesn't have to have a string key; the key could be the actual type. i.e. it could be a Dictionary<Type, Dictionary<String, PropertyInfo>>. That would be faster because you don't have to get the type name, and less error prone - what if I send that method two non-anonymous types with the same name but different namespaces?
Second, you should read Phil Haack's recent blog post about this subject. It contains a full implementation of such a method.