I have a problem with convertion types. My mainForm keeps variable in integer type. Also my form has propertyGrid where I realized property for field (like combobox) with Image & Text. And now I don't understand well how can I convert one type into another. First I need to convert data from int to myProp and then vice versa.
Here setup propertyGrid:
public dashPatternList DashPattern
{
get { return dashPattern; }
set { dashPattern = value; }
}
Here I tried to realize my problem with additional methods:
private dashPatternList dashIN(int dash)
{
dashPatternList ds = dashPatternList.pic0;
if (dash == 1) ds = dashPatternList.pic1;
if (dash == 2) ds = dashPatternList.pic2;
return ds;
}
private int dashOUT(dashPatternList dash)
{
int i = 0;
if (dash == dashPatternList.pic1) i = 1;
if (dash == dashPatternList.pic2) i = 2;
return i;
}
And call it:
pData.DashPattern = dashIN(dashPattern);
dashPattern = dashOUT(pData.DashPattern);
This method works, but maybe you suggest me more easy way.
You could keep the pictures in an array, so instead of dashIN(dash) you'd write dashIN[dash] (and you don't need to write the dashIN function). You just need to initialize it once with something like this:
DashPattern[] dashIN = new DashPattern[] {
dashPatternList.pic0, dashPatternList.pic1, dashPatternList.pic2 };
For the reverse, something like Array.IndexOf(dashIN,mypic) should work.
This way you replace code with data, which tends to be a good thing as it's usually easier to manage. For example now you only have to change one line if you want to change the list of dash patterns, instead of having to change the code in two functions earlier. Plus now it's impossible to make an error that would cause dashOUT(dashIN(dash))!=dash (as would happen if there's a wrong number in the code).
Related
I am trying to set a variable whose value will be a percent (i.e. "20%") which would be a string, and will change based on the level of the element.
To calculate the level I have an integer.
I am trying to use the C# properties to get more used to them and practice, but am uncertain if what I am attempting is allowed.
public int Tier { get; set; } = 1;
private string _ContainerLeft {
get { return _ContainerLeft; }
set
{
int left = (( value - 1) * 2) + 10;
_ContainerLeft = left.ToString() + "%";
}
}
protected override void OnInitialized()
{
base.OnInitialized();
_ContainerLeft = Tier;
}
This is the code for the variable. I take the value passed in (an integer), do some math to calculate the base case and growth, then convert it to the final string.
However, I get errors that "value - 1" can't be performed on a string and int, and on the "Tier" at the bottom that it isn't of type string.
I realize there are plenty of ways around these (although I don't know the best) such as doing some .toString(), int.Parse(), and casting. But I am more concerned with if this is possible without type manipulation.
Fundamentally when you write _ContainerLeft = Tier and int left = ((value -1) * 2) you are trying to do maths with a mixture of integers and strings, which will not work in C#.
Languages like javascript will try to give you some sort of answer (even if it's not what you might expect) but you have to convert from one to another in some way.
Typically I would suggest you make container left a method, and call it using the integer value of Tier
public string ContainerLeft()
{
var left = ((Tier - 1) * 2) + 10;
return $"{left}%"; // string interpolation is nice
}
This will compile fine, and will react to changes in Tier apart from the initial OnInitialized()
edit; Based on responses, I may have been unclear in my final goal. I've updated the last section.
Situation
I have a number of variables which I need to perform the same operation on. In this case, they are strings, and can at the point we reach this code have the value null, "", "Blank", or they could already have an assigned other value that I want to keep.
if (String.IsNullOrEmpty(MyVar1) || "Blank".Equals(MyVar1))
MyVar1 = null;
if(String.IsNullOrEmpty(MyVar2) || "Blank".Equals(MyVar2))
MyVar2 = null;
...
if(String.IsNullOrEmpty(MyVar10) || "Blank".Equals(MyVar10))
MyVar10 = null;
Being a programmer that wants to keep my code clean and this block drives me mad, I'm looking for a way to create a list of these variables, and perform this same if statement + null assignment on each.
For an example, here's what I'd like to do:
MyVar1 = "Blank";
DreamDataStructure varList = new DreamDataStructure() { MyVar1, MyVar2, ..., MyVar10 };
foreach(ref string MyVar in varList)
{
if(String.IsNullOrEmpty(MyVar) || "Blank".Equals(MyVar))
MyVar = null;
}
Console.WriteLine(MyVar1); //Should now be null
What Doesn't Work
1) Because my variables are strings, I can't do something like this.
var myListOfVariables = new[] { &MyVar1, &MyVar2, ..., &MyVar10 };
If I could, I'd be able to foreach over them as expected. Because string is a managed type though, it cannot be passed by reference like this.
2) Similarly, if I just made a List<string> of the variables, they would be passed by value and wouldn't help my case.
3) These variables can't be wrapped in an outer object type, as they need to be used as strings in a large number of places in a legacy application. Assume that it would be too large an effort to change how they're used in every location.
Question
Is there a way to iterate over string (or other managed type) variables in a pass-by-reference way that will allow me to put the entire operation inside of a loop and reduce the duplication of code that's happening here?
The goal here is that I can use the original variables later on in my code with the updated values. MyVar1, etc, are referenced later on already by legacy code which expects them to be null or have an actual value.
If I understand your question correctly, I don't think what you want to do is possible. Please see this question: Interesting "params of ref" feature, any workarounds?
The only thing I can suggest (which I know doesn't answer your question) is creating a method to avoid duplication of your conditional logic:
void Convert(ref string text)
{
if (string.IsNullOrEmpty(text) || "Blank".Equals(text))
{
text = null;
}
}
You could create a function instead of passing references, which would also be more readable.
string Validate(string inputString)
{
return string.IsNullOrEmpty(inputString) || "Blank".Equals(inputString) ? null : inputString;
}
<...>
MyVar1 = Validate(MyVar1);
Update:
Now I get what you're trying to do. You have a bunch of variables, and you want to perform some sort of bulk operation on them without changing anything else. Putting them in a class isn't an option.
In that case you're really stuck operating on them one at a time. There are ways to shorten it, but you're pretty much stuck with the repetition.
I'd
create a string SanitizeString(string input) function
type x = SanitizeString(x); once for each variable
copy and paste the variable names to replace x.
It's lame, but that's about all there is.
Perhaps this would be a better approach. It ensures that the values are always sanitized. Otherwise you can't easily tell whether the values have been sanitized or not:
public class MyValues
{
private string _value1;
private string _value2;
private string _value3;
public string Value1
{
get { return _value1; }
set { _value1 = Sanitize(value); }
}
// repeat for other values
private string Sanitize(string input) =>
string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
}
That's one option. Another is to sanitize the inputs earlier. But ideally we want to ensure that a given class is always in a valid state. We wouldn't want to have an instance of a class whether the values may or may not be valid. It's better to ensure that they are always valid.
ref doesn't really factor into it. We don't need to use it often, if ever. With a value type or string we can just return a new value from a function.
If we're passing a reference type and we want to make changes to it (like setting its properties, adding items to a list) then we're already passing a reference and we don't need to specify ref.
I'd try to write methods first without using ref and only use it if you need to. You probably never will because you'll succeed at whatever you're trying to do without using ref.
Your comment mentioned that this is a legacy app and it's preferable not to modify the existing class. That leaves one more option - reflection. Not my favorite, but when you say "legacy app" I feel your pain. In that case you could do this:
public static class StringSanitizer
{
private static Dictionary<Type, IEnumerable<PropertyInfo>> _stringProperties = new Dictionary<Type, IEnumerable<PropertyInfo>>();
public static void SanitizeStringProperties<T>(T input) where T : class
{
if (!_stringProperties.ContainsKey(typeof(T)))
{
_stringProperties.Add(typeof(T), GetStringProperties(typeof(T)));
}
foreach (var property in _stringProperties[typeof(T)])
{
property.SetValue(input, Sanitize((string)property.GetValue(input)));
}
}
private static string Sanitize(string input)
{
return string.IsNullOrEmpty(input) || string.Equals("Blank", input) ? null : input;
}
private static IEnumerable<PropertyInfo> GetStringProperties(Type type)
{
return type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.Where(property => property.PropertyType == typeof(string) && property.CanRead && property.CanWrite);
}
}
This will take an object, find its string properties, and sanitize them. It will store the string properties in a dictionary by type so that once it has discovered the string properties for a given type it won't have to do it again.
StringSanitizer.SanitizeStringProperties(someObject);
you can simply use a string[] and get the changes back to the caller method like this.
public Main()
{
var myVar1 = "Blank";
var myVar2 = "";
string myVar3 = null;
var myVar4 = "";
string[] dreamDataStructure = new string[] { myVar1, myVar2, myVar3, myVar4 };
}
private void ProcessStrings(string[] list)
{
for(int i = 0; i < list.Length; i++)
{
if (String.IsNullOrEmpty(list[i]) || "Blank".Equals(list[i]))
list[i] = null;
}
}
Am trying to try parse based on a condition between two enums, how do we do this?
bool _isEnum1 = true;
public enum _Enum1
{
value,
Text,
Image
}
public enum _Enum2
{
TextArea,
Button,
Label
}
var _enum = _isEnum1 ? Enum.TryParse("value", out _Enum1 _enum) : Enum.TryParse("TextArea", out _Enum2 _enum)
I want '_enum' to hold right enum type value. Is this possible? Any other alternative is also fine.
Well, you're trying to have the type being dynamically resolved at runtime. You either have to split your entire program flow
if(_isEnum1)
{
Enum1 e = (Enum1)Enum.Parse(typeof(Enum1), "value");
HandleEnum1(e);
}
else
{
Enum2 e = (Enum2)Enum.Parse(typeof(Enum2), "value");
HandleEnum2(e);
}
where HandleEnumX are strongly typed with EnumX, so that the compiler knows what's going on, or go completely crazy overboard and use dynamic
dynamic e;
if(_isEnum1)
{
e = (Enum1)Enum.Parse(typeof(Enum1), s);
}
else
{
e = (Enum2)Enum.Parse(typeof(Enum2), s);
}
// Now e is either Enum1 or Enum2, resolved at runtime.
However, this seems wildly overcomplicated and more of a thought experiment than real code you should be pushing to production. You should probably rethink your design of this part of the program. At the very least, you might want to wrap your enums in a class to allow a more flexible design.
As a side note - you're using TryParse but never testing the returned value, so I took the liberty of exchanging them to Parse.
Enum result;
if (this._isEnum1)
{
result = (_Enum1)Enum.Parse(typeof(_Enum1), "value");
}
else
{
result = (_Enum2)Enum.Parse(typeof(_Enum2), "TextArea");
}
I am trying to create a custom report using Axosoft's Report Builder though I have one big issue to tackle. The report will contain calculated totals and counts of the specific items that are pulled in from the database to display. One such field (Custom_163) is a boolean where true will have it be one type of item (Enhancement) while false will have it as another (Maintenance). I am able to get a count of the total amount of items in the report, but I want to break it down as percentages of the total.
Problem: One boolean field will determine two types of items. Differentiating between them to gather a count of each is where I'm struggling.
Initialized Variables:
private bool _oddRow = true;
private string[] _labels = new string[3];
private int _defectsTotal = 0;
private int _enhanceTotal = 0;
private int _maintenTotal = 0;
Detail Section (where it's creating the rows for the items):
public void Detail1_Format()
{
if (rpt.Fields.Contains("Custom_163") && rpt.Fields["Custom_163"].Value != null)
{
if ((bool)rpt.Fields["Custom_163"].Value == true)
{
//needs something here to add a count towards this type
return enhanceTotal;
}
else
{
//needs something here to add a count towards this type
return maintenTotal;
}
_defectsTotal++;
_enhanceTotal += enhanceTotal;
_maintenTotal += maintenTotal;
// To Color Every Other Row
if (_oddRow)
{
rpt.Sections["Detail1"].BackColor = System.Drawing.Color.FromArgb(224, 224, 224);
}
else
{
rpt.Sections["Detail1"].BackColor = System.Drawing.Color.White;
}
_oddRow = !_oddRow;
// Converting to String Values
if (rpt.Fields["ItemId"].Value == null) return;
}
}
The problem is specifically with these lines:
if((bool)rpt.Fields["Custom_163"].Value == true)
I've changed this a ton from similar conditions:
if(rpt.Fields["Custom_163"].Value.ToString() == "True")
if(rpt.Fields["Custom_163"].Value == true)
if(rpt.Fields["Custom_163"].Value = true)
if(rpt.Fields["Custom_163"].Value == 1)
if(rpt.Fields["Custom_163"].Value.ToString() == "1")
if (Convert.ToBoolean(rpt.Fields["Custom_163"].Value) == true)
etc...
But they don't seem to work as intended. I want it to filter out the items where this field is true so the rest will execute.
Another thing is returning the values from the if-else statement (not working and I've tried using a separate method below that doesn't work either):
public class findTotals
{
public findTotals()
{
}
public int resultTotals()
{
bool item = (bool)rpt.Fields["Custom_163"].Value;
if ( item == true)
{
int enhanceTotal = 1;
return;
}
else
{
int maintenTotal = 1;
return;
}
}
}
(This whole method needs a ton of work TBH).
Lastly, these lines has to be outside of the second if-else or at least somehow returned to the original if in order to be printed to the report:
_enhanceTotal += enhanceTotal;
_maintenTotal += maintenTotal;
The _defectsTotal gets a total count of all the items and that works fine. These two lines though are supposed to add a count based on the above conditions for each type so then the total amount of Enhancements and total amount of Maintenance Items get posted at the end of the report. It doesn't work, if not a casting/conversion error or the conditions force the array(s) out of bounds, it's some other issue. I've played around the order for all these too.
Possible Solutions?
How to safely convert (if needed) the bool values to int so that number can be used to add to a total?
How can I return the values in the if-else so the scope matches up with the variables? Will I need a method?
Thank you in advance.
You should be able to do a string compare against "True". i.e.
if(string.Compare(rpt.Fields["Custom_163"].Value.ToString(), "True") == 0)
I figured out what the issue was.
So basically...
if (Convert.ToBoolean(rpt.Fields["Custom_163"].Value) == true)
{
enhanceTotal++;
}
else
{
maintenTotal++;
}
_defectsTotal++;
(This is all within another if-statement and a bunch of code not relevant to issue.) This works fine and actually returns the count, after that another hurdle was figuring out the labels so it can print into it's placeholders correctly. The whole findTotals method wasn't needed. I didn't need any more variables than the three listed above to make it work. A reason I kept on getting errors was because I tried to use one variable as a label to print into different places within the report which isn't allowed apparently. Instead of using one label and sticking it everywhere, I made several that were identical and made sure to give them different names, etc.
So instead of X in three different report sections, X was X, Y, Z (all identical but different identifer) and stuck it where needed. Man, it seems to obvious now, well thanks again.
I was wondering if there is a more aesthetic/easier to read way to write the following:
for (int i = 0; i < 100; i++)
{
// If m.GetString(i) throws an exception, continue.
// Otherwise, do stuff.
try
{
string s = m.GetString(i);
continue;
}
catch (InvalidCastException)
{
}
// do stuff with the message that you know is not a string.
}
Here's what m looks like:
msg[0] = 10
msg[1] = "string"
msg[2] = 2.224574743
// Etc.
// Assume it's different every time.
So therefore when I do m.GetString(0) in this example, it throws an exception, as msg[0] is a uint and not a string. This is what I use to get the type, since m does not contain a GetType and I cannot edit m.
m is an instance of class Message in a library that I cannot edit.
However, despite this working just fine, it feels inefficient (and certainly not reader-friendly) to intentionally create exceptions, even if it's in a try-catch, in order to get the type.
Is there a better way or am I stuck with this?
Edit: Alright, I researched the Message class some more (which I should have done to begin with, my apologies). It is an IEnumerable<object>
Now that I know that m is an IEnumerable<object>, I think this is probably your best bet:
foreach (string s in m.OfType<string>())
{
// Process s, which can't be null.
}
Nice and simple and it appears to handle all the logic that you want, i.e. it will process only the items in the sequence that are strings, and it will ignore all objects of other types.
However as Servy points out, this will not handle nulls in the list because null does not have any type at all.
[My previous answer before I knew the type of m]
I think you can take one of three approaches to this:
(1) Add a bool TryGetString(int index, out string) method to whatever type m is in your example and then do
if (m.TryGetString(i, out s))
// Process s (need to check for null!)
(2) Add a bool IsString(int index) method and call that before calling GetString().
if (m.IsString(i))
{
s = m.GetString(i);
// Process s (need to check for null!)
(3) Alternatively, you could expose the item via something like GetObject(int index) and then do something like Iiya suggested:
string s = m.GetObject(i) as string;
if (s != null)
// Process s
I think (1) or (3) would be best, although there might be a much better solution that we could suggest if we had more information about m.
If you want to process only strings in a non-strongly typed sequence of data, use next code:
for (int i = 0; i < 100; i++)
{
string s = m[i] as string;
if(s != null)
{
}
}