We have a system that manages generic physical resources. There are over 500 individual resources. The system is used for many different things and to make the software easier to write we use aliases.
For example, a physical resource TG67I9 is given an alias of "RightDoor". When code is written RightDoor is used instead of TG67I9 making the code more readable. This alias list is loaded as a text file with references to resources and their aliases. This system uses literally hundreds of different alias lists to reference the same physical resources.
This type of setup has two major shortcomings. First, when resources are called using their aliases, they are passed in as strings. Door.Open("RightDoor") for example. This does not give any tooltips or smart anything making the code more difficult to write. It basically requires constantly referencing the alias list. Is it RightDoor or Right_Door or right-door or... you get the idea. The second is that there is no validation of parameters until execution. All the compiler knows is that a string is passed in and then it's happy. Only when the code is run, the function tries to access the resource through its alias and fails because it can't find right-door because it's supposed to be RightDoor. An error is displayed. This requires tedious debugging and running the code over and over to weed out any bad aliases.
Is there a better way to do this? Such that an alias list can be made with a cross-reference of physical resources to their alias names and after the list is made that tooltips could appear suggesting resources. (Assume that a new system could be written from scratch)
I'm using the latest .NET with VisualStudio 2017 and C# to write the code.
The simplest approach is most likely a "string enum":
public class Resources {
public const string
LeftDoor = "TG67I8",
RightDoor = "TG67I9";
}
Sample use:
Door.Open(Resources.RightDoor);
Hovering over .RightDoor in VS shows a tooltip (constant) string Resources.RightDoor = "TG67I9"
Right-clicking .RightDoor and selecting Find All References will show where the variable is used.
Another option can be adding the strings in the Resources section of the Project Properties, and then:
using YourProjectNameSpace.Properties;
...
Door.Open(Resources.RightDoor);
That is a bit slower, because the resource(s) are retrieved at run-time, but allows to load the resources from a custom external file separate from the executable.
Use a static class with constants. I have done the same many times and still do. Plus .NET does this as well.
public static class PhysicalResources
{
public const string One = "Uno";
public const string Two = "Deux";
// ...
}
Related
I have written a dll in C# which has five .cs files. ControlsOnForm.cs has a public enum defined in it.
public enum FormControls {
Button,
Label,
DataGrid,
TextBox
}
Now, I'm using this dll in a Windows app which is shown in attached Image1 and when I click Generate button it generates the ControlsOnForm.cs C# file which is same as the enum in dll.
Now how do I refer/use this dynamically generated C# file with enum values in dll.
Or in another words the enum values in ControlsOnForm.cs of dll should get replaced with the newly generated C# file's enum values.
Thanks,
Although it is trivial to get your code to generate a cs file. I suspect what you then want to do is run the resulting code. That step is highly difficult and there are many things you need to understand in terms of the limitation.
However as another user has commented, it sounds like what you want to change is data, and not code.
You should understand the distinction between hardware, firmware, code, configuration and data. The lines are much more blurred than you might first think. But at the end of the day, each is a step in a continuum of changeability. For your purpose, code should be the thing that changes the least often. This in our tool chain it is the hardest to change. Remember at the end of the day everything is ones and zeros... and your data should also change how your program works.
Enums are a collection of named constants. In the same sense that you cannot change a constant, you cannot change an enum (at least without jumping through a ton of hoops).
What you really want to do here is use an external dataset, be it a datatable, an XML configuration file, etc.
On a side note, you would probably want to create an enum outside of a form, but in the same namespace. You can add a module to a project and drop all your enums in there.
From MSDN:
The enum keyword is used to declare an enumeration, a distinct type
that consists of a set of named constants called the enumerator
list.
Usually it is best to define an enum directly within a namespace so
that all classes in the namespace can access it with equal
convenience. However, an enum can also be nested within a class or
struct.
Reference: MSDN Enum Entry
In the application I'm developing, the same software package serves many industries, and those industries have different vocabulary for what is essentially the same thing. The application is a C# server, to which a WPF desktop app makes socket-based XML requests.
For example, some customers may call something an "Item", some call it a "Part", or some call it a "SKU".
The goal is for the application to be able to relabel itself based on a "Vocabulary" setting that we create for the user. Typically, a given customer's vocabulary will only differ by perhaps 5-25 words/phrases out of the entire application. These custom vocabularies are specific to/created by the customer, and wouldn't be kept with the main application distribution.
My initial thought was to do this with custom CultureInfo, (e.g. "en-AC" for "Acme" company), supply just values that differ from the base en-US in that resource file.
The en-AC.resx resource could be kept on the server, loaded by the server, and also transmitted for loading into the WPF client app.
Problem with that thus far seems to be that the ResourceManager does not correctly pick strings for custom cultures, a'la this thread, and I've not been able to solve that yet. As well, as the app is ClickOnce deployed, we may not have permission to register a new culture.
My next thought, since the number of phrases to modify is so small, was to replace the resource value at runtime, but that seems to be a bit of a no-no as well, searching around.
So, thought I would ask the community for their suggestions on how to handle this.
Open to suggestions and ideas...
Because it's only about a few words I think I'd do it via a naming convention. Suppose you defined the string key "MyCompany". You usually access this way:
string myString1 = Properties.Resource.MyCompany;
But it is also ok to Access it that way:
string myString2 = Properties.Resource.ResourceManager.GetString ("MyCompany")
It's exactly the same (but dealing with strings as identifiers - which is somewhat error prone). What you now can do is to check for a special name first that you syntesize like "MyCompany_AC". The drawback is you need your own wrapper for each string:
string MyCompany
{
get
{
string myString = Properties.Resource.ResourceManager.GetString ("MyCompany_" + theCompanyPostfix);
if (myString == null)
{
myString = Properties.Resource.ResourceManager.GetString ("MyCompany");
}
return myString;
}
}
I have some constants that i would like to have at application level, like stored procedure names, user messages, and others. there is very less chance that i will ever change these resources.
Please let me know what is good practice for keeping constants in our application.
Is Resource dictionary is preferable over .cs file.
Regards
AA
For starters, you're on the right track thinking about this stuff at all. Magic strings and other magic values are bad for lots of reasons.
Here are some guidelines we use:
Constants are only allowed for things that are REAL-WORLD constant. If you need to use "My bonnie lies over the {0}" as a format string, you use a RESOURCE for that.
Things that might change, EVER, are not constants. You have a few options for this stuff.
If it isn't part of your logic, it doesn't go in source code. It goes in one of the following externals locations and gets referenced in source code so that you don't have to recompile to make a change.
We generally have three files per assembly, as needed:
First, a constants file. This is usually as simple as Constants.cs. Put your constants (AND readonly statics that are not compile-time constant and never change) in this file. You could also include things that are configurable but MUST have a default value.
internal class Constants
{
public const LogLevel DEFAULT_LOG_LEVEL = LogLevel.Error;
public static readonly string APP_NAME = Configuration.ApplicationName ?? "Test Application";
}
Second, a file that reads configuration values and returns them as static values. This is generally Configuration.cs and is responsible for return ALL configuration values. This saves you from having to recompile to change a connection string, deal with a setting, or something else. The actual values reside in places like an .ini file, web.config or app.config, database table, or other location outside the source code. If the following example you could smatter ConfigurationManager.AppSettings["ApplicationName"] all over your code, but then what if you want to change the key of that appsetting? You have to find an rename all the reference to it. Just take the extra 30 seconds to do something like this, and all Configuration.ApplicationName.
internal class Configuration
{
public static string ApplicationName
{
get
{
return ConfigurationManager.AppSettings["ApplicationName"];
}
}
}
Finally, one or more resource files. This is where we put things like icons, images, unusual fonts, localized (or just changable) strings for display purposes, etc...
There is no specific right way do do this stuff, but I think the above will give you a place to start.
What will be the Regular Expression to get all the property and variables names of any class in c#, I want to parse the *.cs file. that is i want to select any *.cs file as input and it should get the property name of that selected class, as an output.
can any one help!!!....would appreciate for any help i tried very much but not got the actual result every time class name is coming instead of property.
thanks
Jack
There's no way you're going to be able to get exactly what you want with a regular expression because you need semantic context, not just string parsing.
For example, a good first attempt at finding all of the field and property definitions in a C# file might go something like this
^\s*(?:(?:private|public|protected|internal)\s+)?(?:static\s+)?(?:readonly\s+)?(\w+)\s+(\w+)\s*[^(]
That will match properties (public int Foo {...}) and fields (private int foo;) but not methods (protected void Bar()).
The problem is that a regex engine has no concept of the context within which those tokens appear. It will match both foo and bar in this code:
int foo;
void Stuff()
{
int bar;
}
If you happen to know that your code file follows some coding standards, you may have more luck. For example, if you enforce a style rule that all class members must have access specifiers, then you can make the private/public/etc part of that regex non-optional; since those are only permitted at the class level, it will filter out local variables.
There are other options, none of them too attractive at first glance. There is persistent talk from the C# dev team about exposing the C# compiler as a service in some future version of .NET, which would be perfect here, but I wouldn't expect that any time soon. You could purchase a third-party C# parser/analyzer like this one (caveat: I have zero experience with that, it's just the first Google hit). You could try compiling the .cs file using csc and examining the IL, but you'd need to know all of the third-party references.
Let's say I have an application with two files.
Console.cs and Business.cs
Console.cs has program Main class.
Business.cs has three classes named Customer, Order and Orderline.
Is there anyway in C# to determine at runtime (maybe with reflection) that the business objects are in a file named Business.cs?
The C# compiler does not emit this information into the DLL, so it's not available through reflection. However, as you'll be aware from debugging, the debugger can match up compiled locations to source code locations. It does this through PDB files. So it might be theoertically possible for you to ship your PDB files, and invoke the unmanaged debugger or diagnostic symbol store API (see General Reference > Unmanaged API Reference in MSDN) to determine where a given method was defined. You can't really do this for a class, though, because a class could be spread across multiple files using partial classes.
If you compile in debug mode you can probably use Cecil (part of Mono project) to extract the source filenames from the debug symbols. But when you compile in release mode this information probably gets lost.
However, if you need to do this, for other purposes than for example static analysis of your software, you are probably on the wrong track and should think of another solution.
If you put the classes in a Business namespace you could use reflection to find if an object comes from that namespace:
namespace Business {
class Customer {}
class Order {}
class OrderLine {}
}
var myObject = new Customer();
Console.WriteLine(myObject.GetType().Namespace); // writes "Business"
I believe the closest you'll get is typeof(Customer).Assembly.Location. However, this will only give you the DLL, not the location of the source code (which makes sense, since the source code would normally not be included with the binaries).
*.PDB (debug info files) files should have that information. Otherwise I see no way to get it, since code files is just an abstraction which compiled code should not care about.
not sure what your use case is, however if some one is calling you then you can add
compiler directives
[CallerFilePath] string file = "", [CallerLineNumber] int LineNo = 0
in your method.
if not than your best way of accessing this is by using the .pdb file that get's generated. The format is published and a C++ dll is available that can be used to access the file however the easiest way to read the file (and possible line number) if included in the pdb file is using stacktrace
You can access the stack in an exception, so if a class allows you to throw an exception by passing null where you should not than try catch it and you have your stack trace.
if you need the calling file but do not want to add the compiler directives as some one can simply overwrite it you can do something like:
StackTrace st = new StackTrace(new StackFrame(1));
st.GetFrame(1).GetFileName());
Assuming :
You have a project (probably technical with some extension methods etc) that all other projects in your solution reference (let's name it "NameSpaceGloballyVisibleByAllProjects")
You are using SDK-style csproj for your project (otherwise you'll have a little more work)
People in your team code without doing fancy things (ie: "class" / "struct"/ "enum" keywords are at the beginning of their own line in your .cs files).
It means that, by adding this class in "NameSpaceGloballyVisibleByAllProjects":
using System;
using System.Runtime.CompilerServices;
namespace NameSpaceGloballyVisibleByAllProjects
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum, AllowMultiple = true, Inherited = false)]
public class MemorizeFilePathAttribute : Attribute
{
public string Path() { return _filepath; }
public MemorizeFilePathAttribute([CallerFilePath] string filepath = "")
{
_filepath = filepath;
}
readonly string _filepath;
}
}
You can simply use it like this:
using System.Reflection;
using NameSpaceGloballyVisibleByAllProjects;
Type type = typeof(Program);
var files = type.GetCustomAttributes<MemorizeFilePathAttribute>(false).Select(att => att.Path).ToList();
Note: As you notice there are more than one file! This is because of "partial" keyword in C#. So it's up to you to use "files.Single()" or not...
We just need to add this attribute above all types now
We can do that in Visual Studio with Ctr-H (Find-and-Replace).
Select All Solution
Check options "regex" & "case sensitive"
Find: "^( *)([a-z][a-z ]*)? (class|struct|enum) "
(without double quotes, but with the final ' '!)
Replace by: "$1 [NameSpaceGloballyVisibleByAllProjects.MemorizeFilePath]\r\n$1$2 $3 "
(without double quotes, but with the final ' '!)
Be ready for this to take a little time (Go get a coffe... or tea)