I just had this idea for something that I'd love to be able to use:
Let's say I have to fix a bug and I decide to write an ugly code line that fixes the immediate problem - but only because I promise myself that I will soon find the time to perform a proper refactoring.
I want to be able to somehow mark that code line as "Expired in" and add a date - so that if the code is compiled some time after that date there will be a compilation error/warning with a proper message.
Any suggestions? It must be possible to perform - maybe using some complicated #IF or some options in visual studio?
I'm using VS 2005 - mainly for C#.
Mark the code with the System.ObsoleteAttribute attribute, you'll get a compiler warning, which will nag you to fix the code
[Obsolete("You've an ugly hack here")]
public void MyUglyHack()
{
...
}
Alternatively . . .
Write your own attribute, passing it an expiration date on the constructor, in the constructor throw an exception if DateTime.Now >= expirationDate.
The compile will fail until you fix the code (or more likely increase the expiration date, or far more likely you just remove the Attribute.
oooohhh - this is 'orrible. try this for a giggle:
[AttributeUsage(AttributeTargets.All)]
public class BugExpiryAttribute : System.Attribute
{
// don't tell 'anyone' about this hack attribute!!
public BugExpiryAttribute(string bugAuthor, string expiryDate)
{
DateTime convertedDate = DateTime.Parse(expiryDate);
Debug.Assert(DateTime.Now <= convertedDate,
string.Format("{0} promised to remove this by {1}",
bugAuthor, convertedDate.ToString("dd-MMM-yyyy")));
}
}
then, decorate your method/class etc:
[BugExpiryAttribute("Jack Skit", "2011-01-01")]
public static void Main(string[] args)
{
...
}
... nasty :-)
[DISCLAIMER] - created in the name of academic interest, not production code finese!!
[edit] - just to clarify, code compiled and in production will continue to run on/after the 'bugExpriryDate'. only once the code is run in the compiler (on/after the date), will the warning message be raised (debug.assert). just thought it worth making that distinction - cheers MartinStettner.
[caveat] - if used in classes/methods etc would need to be read via reflection. however (and this is interesting) will work straight off in the compiler if used on sub Main(). how strange!! (thanks for the nod Hans...)
I think this is the reason Visual Studio has a Task List. Add the comment:
\\ TODO: Fix this spaghetti by 01APR11
and it will show up like this
.
the keywords are configurable from the options
You could write comment lines in the form
// Expires on 2011/07/01
and add a prebuild step which does a solution-wide replace of these lines by something like
#error Code expired on 2011/07/01
for all lines that contain a date before the current day. For this prebuild step you would need to write a short program (probably using regular expressions and some date comparision logic)
This step could also be performed by a VS macro, which allows for easier access to all files fo the solution but has the disadvantage that it must be installed and run on all VS installations where your project is compiled.
One more option if you have unit tests for your code you can time bomb the tests that verifies your fix. This way you don't introduce strange checks in your production code.
Also I think the best option if you have to put in hack (you've probably already spent enough time looking at it to fix properly... but still want a hack there) than open bug/create task/work item (whatever you use to track future work) and decide if you want to fix it later.
Well it doesn't do exactly what you're asking for but you could use a Debug.Assert() method call which would alert you (in Debug only) that the code has expired. One benefit would be that it wouldn't inadvertently affect your production code (compilation or execution) but would be sufficiently annoying in Debug for you to want to correct it.
// Alert the developer after 01/07/2011
Debug.Assert(Date.Now < new DateTime(2011, 7, 1))
With .NET 6+ this is quite simple when a source generator is used:
Source generator
[Generator]
public class ObsoleteFromDateSourceGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var currentDate = DateTime.Now.Date;
ImmutableArray<AttributeSyntax> attributes = context.Compilation
.SyntaxTrees.SelectMany(s => s.GetRoot().DescendantNodes())
.Where((d) => d.IsKind(SyntaxKind.Attribute))
.OfType<AttributeSyntax>()
.Where(d => d.Name.ToString() == SourceGeneratorConstants.ObsoleteFromDateAttributeName)
.ToImmutableArray();
foreach (var attribute in attributes)
{
try
{
var semanticModel = context.Compilation.GetSemanticModel(attribute.SyntaxTree);
var argumentDate = attribute.ArgumentList?.Arguments.FirstOrDefault()?.Expression;
var argumentMessage = attribute.ArgumentList?.Arguments.Skip(1).FirstOrDefault()?.Expression;
if (argumentDate != null)
{
var date = DateTime.ParseExact(semanticModel.GetConstantValue(argumentDate).ToString(), SourceGeneratorConstants.ObsoleteFromDateAttributeDateFormat, CultureInfo.InvariantCulture);
string? message = null;
if (argumentMessage is not null
&& semanticModel.GetConstantValue(argumentMessage) is Optional<object> tmp
&& tmp.HasValue)
{
message = tmp.Value?.ToString();
}
var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ObsoleteFromDate(message, /*isWarning:*/ currentDate < date), attribute.GetLocation());
context.ReportDiagnostic(diagnostic);
}
else
{
throw new ArgumentNullException(paramName: "date");
}
}
catch (Exception ex)
{
var diagnostic = Diagnostic.Create(DiagnosticDescriptors.ObsoleteFromDateError(ex.Message), attribute.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
}
public void Initialize(GeneratorInitializationContext context)
{
}
}
Some constants
public static class SourceGeneratorConstants
{
public const string ObsoleteFromDateAttributeName = "ObsoleteFromDate";
public const string ObsoleteFromDateAttributeDateFormat = "yyyy-MM-dd";
}
Build diagnostics
public static class DiagnosticDescriptors
{
public const string Category = "BNX";
public const string ErrorTitle = "Source code error";
public static readonly Func<string?, bool, DiagnosticDescriptor> ObsoleteFromDate = (message, isWarning) => new DiagnosticDescriptor(
id: $"{Category}{SourceGeneratorConstants.ObsoleteFromDateAttributeName}",
title: "Obsolete code",
messageFormat: message ?? "Obsolete code, please review",
category: Category,
defaultSeverity: isWarning ? DiagnosticSeverity.Warning : DiagnosticSeverity.Error,
isEnabledByDefault: true);
public static readonly Func<string?, DiagnosticDescriptor> ObsoleteFromDateError = (message) => new DiagnosticDescriptor(
id: $"{Category}{SourceGeneratorConstants.ObsoleteFromDateAttributeName}",
title: ErrorTitle,
messageFormat: $"Unable to parse {SourceGeneratorConstants.ObsoleteFromDateAttributeName} attribute because of error: {message} Expecting the following syntax: [{SourceGeneratorConstants.ObsoleteFromDateAttributeName}(\"{SourceGeneratorConstants.ObsoleteFromDateAttributeDateFormat}\", \"message\")]",
category: Category,
defaultSeverity: DiagnosticSeverity.Error,
isEnabledByDefault: true);
}
A custom attribute
/// <summary>
/// Triggers a build error at and after a specific system date. Source generators must be included in the project.
/// </summary>
public class ObsoleteFromDateAttribute : Attribute
{
public const string DateFormat = "yyyy-MM-dd";
/// <summary>
/// Build error message.
/// </summary>
public string Message { get; }
/// <summary>
/// System date at and after which the build error should occur.
/// </summary>
public string Date { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ObsoleteFromDateAttribute"/> class.
/// </summary>
/// <param name="date">Required. System date at and after which the build error should occur. Expected format: <see cref="DateFormat"/></param>
/// <param name="message">Optional. Build error message.</param>
public ObsoleteFromDateAttribute(string date, string message): base()
{
Date = date;
Message = message;
}
}
And finally a test:
// The following should result in a build error
[ObsoleteFromDate("2000-01-01", "We are waiting for some Jira thing 1 to complete")]
public class ObsoleteClass1
{
}
// The following should result in a build warning
[ObsoleteFromDate("3000-01-01", "We are waiting for some Jira thing 2 to complete")]
public class ObsoleteClass2
{
}
Without controlling the compiler (possible in the 5.0 timeframe with compiler as a service?), you are not going to have your code expire. You can mark the code as deprecated, or use the Obsolete attribute, or similar, to fire off a warning, but people can ignore warnings (many devs I have met have not learned the rule that warnings are errors).
I think it is a lot of work to try to protect people from themselves. It is even harder when you are protecting them from themselves in the future. Mark the code as a kludge and leave it at that.
Instead of embedding a time bomb, perhaps consider applying a BUGBUG: comment?
Rather than forcing you or someone else to fix code that may be kind of unsightly but works as expected down the road, you can just do a solution-wide search and find the ugly bits when you decide it's time to get down and refactor the really ugly stuff.
Track it in a bug instead. Then it can be properly scheduled and prioritized with other refactoring work.
TODO comments in code can have a tendency to be lost and forgotten. Throwing a compiler error after a particular date will likely lead to that date being pushed forward, or the comment/attribute removed.
I hope i can help with this. take 2 datetimepicker on tool box. And just convert 1 datetimepicker.
private void expired()
{
DateTime expired = DateTime.Parse(Convert.ToDateTime(datetimepicker1.Text).ToString());
DateTime compare = DateTime.Parse(Convert.ToDateTime(datetimepicker2.Text).ToString());
if(expired < compare)
{
MessageBox.Show("This product is expired!");
}
else
}
MessageBox.Show("This product is not expired");
{
}
Both TIME and DATE emit strings and, to my knowledge, there is no way to parse them out at the preprocessing stage.
There are a few methods you can easily do in code to ensure that the code at least warns you at run time. Including an assert is one way, putting in a code comment also works, but the way I handle it is through including a doxygen comment with a note explaining that the function contains a hack, bug, or performance issue that needs to be resolved. This ends up getting filtered by many programmers and is easily viewable on the website for myself or other people to fix.
Related
At this moment I'm writing Roslyn analyzer with fixer that checks if constructor arguments are checked for null. And if not, fixer will add that check.
So, basically for code
public Foo(string param1)
{
}
it should produce
public Foo(string param1)
{
if (param1 == null)
throw new ArumentNullException(nameof(param1));
}
But in some cases code style could be different and it is desired to get "if" with braces like
public Foo(string param1)
{
if (param1 == null)
{
throw new ArumentNullException(nameof(param1));
}
}
or even (not sure if there any settings for that, maybe I'll add separate fixer for it):
public Foo(string param1)
{
this.param1 = param1 ?? throw new ArumentNullException(nameof(param1));
}
So, there are many options for a possible fix and I wonder - is it possible to read code style settings inside of fixer? I've checked
DocumentOptionSet options = context.Document.GetOptionsAsync()
but didn't found how to use it properly and not even sure that it is what I need.
Will appreciate any tips and ideas
UPDATE 1
So, seem that I found how it is supposed to work:
options.GetOption(CodeStyleOptions.QualifyFieldAccess);
And I can see (CSharpCodeStyleOptions.cs) with needed option PreferBraces. However, this class is internal and I can't use it in fixer.
UPDATE 2
I tried Simplifier.ReduceAsync() method, but seem that it doesn't do what I need.
UPDATE 3
So, it seems that we all treat that property wrong, because of its naming.
Here is my issue in Roslyn github. The main idea - "Prefer braces" option should be named "Require braces". New issue for this change.
And it is not supposed to get these settings from analyzer/fixer code.
So I have a cmdlet written in c#: Get-LivingCharacter. I want users to use this like Get-LivingCharacter -Name "Bran", but I would like to allow for the list of available characters to change. Maybe today, "Bran" is a valid name to pass in for Get-LivingCharacter, but maybe in the future it will not be. Things happen.
For convenience I want to allow tab-completion of this field. However, I can't seem to get that to work for non-const data sets. Dynamic fields don't even auto-complete the field name, nevermind the value, and I don't know a way to implement this for a non-dynamic field. Conceptually, I could generate a .ps1 file on startup given the current data set, and then load that ps1 as the module, but this feels a bit like killing a pup with a greatsword - lots of overkill. Is there a better option?
I had already implemented a similar function to the DynamicParam helper function, as reference in the comments. However, tab completion wasn't working. I was writing a minimal reproduction example, when...my tab completion worked.
It turns out, it reproducibly works/breaks based on the inclusion of a WriteDebug statement:
[Cmdlet("Get", "LivingCharacter")]
public class GetLivingCharacter : Cmdlet, IDynamicParameters
{
protected override void ProcessRecord()
{
}
public object GetDynamicParameters()
{
WriteDebug("Getting names"); // Tab completion won't work with this here - comment it out and it works.
^^^^^^^^^^
var chars = new List<String>() { "Bran", "Arya" };
var dict = new RuntimeDefinedParameterDictionary();
var attributes = new Collection<Attribute>
{
new ParameterAttribute
{
HelpMessage = "Enter a valid open name",
Mandatory = true
},
new ValidateSetAttribute(chars.ToArray()),
};
dict.Add("Name", new RuntimeDefinedParameter("Name", typeof(string), attributes));
return dict;
}
}
After some digging, the WriteDebug statement is throwing (which I assume is because it can't output while I'm typing). It then recreates the GetLivingCharacter class after I've finished the command to validate. It took a while to find since, because of the issue, I can't write the error to the console, so I had to append to a temp file instead.
I have a specific question at the end but I want to provide plenty of background and context so that readers can understand my objective.
Background
I am building a console-style application with ASP.NET MVC 3. The concept itself is simple: receive command strings from the client, check if the command supplied exists and if the arguments provided with the command are valid, execute the command, return a set of results.
Inner-workings
With this application I decided to get a little creative. The most obvious solution to a terminal-style application is to build the world's largest IF statement. Run every command through the IF statement and call the appropriate functions from within. I did not like this idea. In an older version of the application this was how it operated and it was a huge mess. Adding functionality to the application was ridiculously difficult.
After much thought I decided to build a custom object called a command module. The idea is to build this command module with each request. The module object would contain all available commands as methods and the site would then use reflection to check if a command supplied by the user matches a method name. The command module object sits behind an interface called ICommandModule shown below.
namespace U413.Business.Interfaces
{
/// <summary>
/// All command modules must ultimately inherit from ICommandModule.
/// </summary>
public interface ICommandModule
{
/// <summary>
/// The method that will locate and execute a given command and pass in all relevant arguments.
/// </summary>
/// <param name="command">The command to locate and execute.</param>
/// <param name="args">A list of relevant arguments.</param>
/// <param name="commandContext">The current command context.</param>
/// <param name="controller">The current controller.</param>
/// <returns>A result object to be passed back tot he client.</returns>
object InvokeCommand(string command, List<string> args, CommandContext commandContext, Controller controller);
}
}
The InvokeCommand() method is the only method on the command module that my MVC controller is immediately aware of. It is then this method's responsibility to use reflection and look at the instance of itself and locate all available command methods.
I use Ninject for dependency injection. My MVC controller has a constructor dependency on ICommandModule. I built a custom Ninject provder that builds this command module when resolving the ICommandModule dependency. There are 4 types of command modules Ninject can build:
VisitorCommandModule
UserCommandModule
ModeratorCommandModule
AdministratorCommandModule
There is one more class BaseCommandModule which all other module classes inherit from. Real quickly, here are the inheritance relationships:
BaseCommandModule : ICommandModule
VisitorCommandModule : BaseCommandModule
UserCommandModule : BaseCommandModule
ModeratorCommandModule : UserCommandModule
AdministratorCommandModule : ModeratorCommandModule
Hopefully you can see how this is constructed by now. Based on the user's membership status (not logged in, regular user, moderator, etc) Ninject will provide the proper command module with only the command methods the user should have access to.
All of this works great. My dilemma comes in when I am parsing the command string and figuring out how to structure the command methods on the command module object.
The Question
How should the command string be parsed and executed?
Current Solution
Currently I break up the command string (the string passed in by the user containing the command and all arguments) in the MVC controller. I then call the InvokeCommand() method on my injected ICommandModule and I pass in a string command and a List<string> args.
Let's say I have the following command:
TOPIC <id> [page #] [reply “reply”]
This line defines the TOPIC command accepting a required ID number, an optional page number, and an optional reply command with a reply value.
I currently implement the command method like this (The attributes above the method are for help menu information. The HELP command uses reflection to read all these and display an organized help menu):
/// <summary>
/// Shows a topic and all replies to that topic.
/// </summary>
/// <param name="args">A string list of user-supplied arguments.</param>
[CommandInfo("Displays a topic and its replies.")]
[CommandArgInfo(Name="ID", Description="Specify topic ID to display the topic and all associated replies.", RequiredArgument=true)]
[CommandArgInfo(Name="REPLY \"reply\"", Description="Subcommands can be used to navigate pages, reply to the topic, edit topic or a reply, or delete topic or a reply.", RequiredArgument=false)]
public void TOPIC(List<string> args)
{
if ((args.Count == 1) && (args[0].IsInt64()))
TOPIC_Execute(args); // View the topic.
else if ((args.Count == 2) && (args[0].IsInt64()))
if (args[1].ToLower() == "reply")
TOPIC_ReplyPrompt(args); // Prompt user to input reply content.
else
_result.DisplayArray.Add("Subcommand Not Found");
else if ((args.Count >= 3) && (args[0].IsInt64()))
if (args[1].ToLower() == "reply")
TOPIC_ReplyExecute(args); // Post user's reply to the topic.
else
_result.DisplayArray.Add("Subcommand Not Found");
else
_result.DisplayArray.Add("Subcommand Not Found");
}
My current implementation is a huge mess. I wanted to avoid giant IF statements, but all I did was trade one giant IF statement for all the commands, for a ton of slightly less giant IF statements for every command and its arguments. This isn't even the half of it; I simplified this command for this question. In actual implementation there are quite a few more arguments that can be provided with this command and that IF statement is the ugliest thing I have ever seen. It's very redundant and not at all DRY (don't repeat yourself) as I have to display "Subcommand Not Found" in three different places.
Suffice it to say, I need a better solution than this.
The Ideal Implementation
Ideally I would love to structure my command methods something like his:
public void TOPIC(int Id, int? page)
{
// Display topic to user, at specific page number if supplied.
}
public void TOPIC(int Id, string reply)
{
if (reply == null)
{
// prompt user for reply text.
}
else
{
// Add reply to topic.
}
}
Then I'd love to do this:
Receive command string from client.
Pass command string directly into InvokeCommand() on ICommandModule.
InvokeCommand() performs some magic parsing and reflection to choose the right command method with the right arguments and invokes that method, passing in only the necessary arguments.
The Dilemma with the Ideal Implementation
I'm not sure how to structure this logic. I've been scratching my head for days. I wish I had a second pair of eyes to help me out on this (hence finally resorting to a novel of an SO question). In what order should things happen?
Should I pull out the command, find all methods with that command name, then loop through all the possible arguments, then loop through my command string's arguments? How do I determine what goes where and what arguments go in pairs. For instance, if I loop through my command string and find Reply "reply" how do I pair the reply content with the reply variable, while encountering <ID> number and supplying it for the Id argument?
I'm sure I'm confusing the hell out of you now. Let me illustrate with some examples of command strings the user might pass in:
TOPIC 36 reply // Should prompt the user to enter reply text.
TOPIC 36 reply "Hey what's up?" // Should post a reply to the topic.
TOPIC 36 // Should display page 1 of the topic.
TOPIC 36 page 4 // Should display page 4 of the topic.
How do I know to send 36 to the Id parameter? How do I know to pair reply with "Hey what's up?" and pass "Hey what's up?" as the value for the reply argument on the method?
In order to know which method overload to call I need to know how many arguments where supplied so that I can match that number to the overload of the command method that takes that same number of arguments. The problem is, `TOPIC 36 reply "Hey what's up?" is actually two arguments, not three as reply and "Hey..." go together as one argument.
I don't mind bloating the InvokeCommand() method a little (or a lot) as long as it means that all the complex parsing and reflection nonsense is handled there and my command methods can remain nice and clean and easy to write.
I guess I'm really just looking for some insight here. Does anyone have any creative ideas to solve this problem? It really is a big issue because the argument IF statements are currently making it very complicated to write new commands for the application. The commands are the one part of the application that I want to be super simple so that they can be easily extended and updated. Here is what the actual TOPIC command method looks like in my app:
/// <summary>
/// Shows a topic and all replies to that topic.
/// </summary>
/// <param name="args">A string list of user-supplied arguments.</param>
[CommandInfo("Displays a topic and its replies.")]
[CommandArgInfo("ID", "Specify topic ID to display the topic and all associated replies.", true, 0)]
[CommandArgInfo("Page#/REPLY/EDIT/DELETE [Reply ID]", "Subcommands can be used to navigate pages, reply to the topic, edit topic or a reply, or delete topic or a reply.", false, 1)]
public void TOPIC(List<string> args)
{
if ((args.Count == 1) && (args[0].IsLong()))
TOPIC_Execute(args);
else if ((args.Count == 2) && (args[0].IsLong()))
if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
TOPIC_ReplyPrompt(args);
else if (args[1].ToLower() == "edit")
TOPIC_EditPrompt(args);
else if (args[1].ToLower() == "delete")
TOPIC_DeletePrompt(args);
else
TOPIC_Execute(args);
else if ((args.Count == 3) && (args[0].IsLong()))
if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
TOPIC_EditReplyPrompt(args);
else if ((args[1].ToLower() == "delete") && (args[2].IsLong()))
TOPIC_DeleteReply(args);
else if (args[1].ToLower() == "edit")
TOPIC_EditExecute(args);
else if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
TOPIC_ReplyExecute(args);
else if (args[1].ToLower() == "delete")
TOPIC_DeleteExecute(args);
else
_result.DisplayArray.Add(DisplayObject.InvalidArguments);
else if ((args.Count >= 3) && (args[0].IsLong()))
if (args[1].ToLower() == "reply" || args[1].ToLower() == "modreply")
TOPIC_ReplyExecute(args);
else if ((args[1].ToLower() == "edit") && (args[2].IsLong()))
TOPIC_EditReplyExecute(args);
else if (args[1].ToLower() == "edit")
TOPIC_EditExecute(args);
else
_result.DisplayArray.Add(DisplayObject.InvalidArguments);
else
_result.DisplayArray.Add(DisplayObject.InvalidArguments);
}
Isn't that ridiculous? Every command has a monster like this and it's unacceptable. I am just going over scenarios in my head and how code might handle it. I was pretty proud of my command module setup, now if I could just be proud of the command method implementation.
While I'm not looking to jump ship with my entire model (command modules) for the application, I am definitely open to suggestions. I'm mostly interested in suggestions related to parsing the command line string and mapping its arguments to the right method overloads. I'm sure whatever solution I go with will require a fair amount of redesign so don't be afraid to suggest anything you think is valuable; even if I don't necessarily use your suggestion, it may put me on the right track.
Further Clarifications
I just wanted to clarify real quick that the mapping of commands to command methods is not really something I'm worried about. I'm mostly concerned about how to parse and organize the command line string. Currently the InvokeCommand() method employs some very simple C# reflection to find the appropriate methods:
/// <summary>
/// Invokes the specified command method and passes it a list of user-supplied arguments.
/// </summary>
/// <param name="command">The name of the command to be executed.</param>
/// <param name="args">A string list of user-supplied arguments.</param>
/// <param name="commandContext">The current command context.</param>
/// <param name="controller">The current controller.</param>
/// <returns>The modified result object to be sent to the client.</returns>
public object InvokeCommand(string command, List<string> args, CommandContext commandContext, Controller controller)
{
_result.CurrentContext = commandContext;
_controller = controller;
MethodInfo commandModuleMethods = this.GetType().GetMethod(command.ToUpper());
if (commandModuleMethods != null)
{
commandModuleMethods.Invoke(this, new object[] { args });
return _result;
}
else
return null;
}
So as you can see, I'm not worried about how to find the command methods as that is already working. I'm just pondering a good way to parse the command string, organize arguments, and then using that information to pick the right command method/overload using reflection.
Final Design Goal
I am looking for a really good way to parse the command string I'm passing in. I want the parser to identify several things:
Options. Identify options in the command string.
Name/Value Pairs. Identify name/value pairs (e.g. [page #] <- includes keyword "page" and value "#")
Value Only. Identify value only.
I want these to be identified via metadata on the first command method overload. Here is a list of sample methods I want to write, decorated with some metadata to be used by the parser when it is doing reflection. I will give you these method samples and some sample command strings that should map to that method. That information should aid me in formulating a good parser solution.
// Metadata to be used by the HELP command when displaying HELP menu, and by the
// command string parser when deciding what types of arguments to look for in the
// string. I want to place these above the first overload of a command method.
// I don't want to do an attribute on each argument as some arguments get passed
// into multiple overloads, so instead the attribute just has a name property
// that is set to the name of the argument. Same name the user should type as well
// when supplying a name/value pair argument (e.g. Page 3).
[CommandInfo("Test command tests things.")]
[ArgInfo(
Name="ID",
Description="The ID of the topic.",
ArgType=ArgType.ValueOnly,
Optional=false
)]
[ArgInfo(
Name="PAGE",
Description="The page number of the topic.",
ArgType=ArgType.NameValuePair,
Optional=true
)]
[ArgInfo(
Name="REPLY",
Description="Context shortcut to execute a reply.",
ArgType=ArgType.NameValuePair,
Optional=true
)]
[ArgInfo(
Name="OPTIONS",
Description="One or more options.",
ArgType=ArgType.MultiOption,
Optional=true
PossibleValues=
{
{ "-S", "Sort by page" },
{ "-R", "Refresh page" },
{ "-F", "Follow topic." }
}
)]
[ArgInfo(
Name="SUBCOMMAND",
Description="One of several possible subcommands.",
ArgType=ArgType.SingleOption,
Optional=true
PossibleValues=
{
{ "NEXT", "Advance current page by one." },
{ "PREV", "Go back a page." },
{ "FIRST", "Go to first page." },
{ "LAST", "Go to last page." }
}
)]
public void TOPIC(int id)
{
// Example Command String: "TOPIC 13"
}
public void TOPIC(int id, int page)
{
// Example Command String: "TOPIC 13 page 2"
}
public void TOPIC(int id, string reply)
{
// Example Command String: TOPIC 13 reply "reply"
// Just a shortcut argument to another command.
// Executes actual reply command.
REPLY(id, reply, { "-T" });
}
public void TOPIC(int id, List<string> options)
{
// options collection should contain a list of supplied options
Example Command String: "TOPIC 13 -S",
"TOPIC 13 -S -R",
"TOPIC 13 -R -S -F",
etc...
}
The parser must take in a command string, use reflection to find all possible command method overloads, use reflection to read the argument attributes to help determine how to divide up the string into a proper list of arguments, then invoke the proper command method overload, passing in the proper arguments.
Take a look at Mono.Options. It's currently part of Mono framework but can be downloaded and used as a single library.
You can obtain it here, or you can grab the current version used in Mono as a single file.
string data = null;
bool help = false;
int verbose = 0;
var p = new OptionSet () {
{ "file=", v => data = v },
{ "v|verbose", v => { ++verbose } },
{ "h|?|help", v => help = v != null },
};
List<string> extra = p.Parse (args);
The solution I generally use looks something like this. Please ignore my syntax errors... been a few months since I've used C#. Basically, replace the if/else/switch with a System.Collections.Generic.Dictionary<string, /* Blah Blah */> lookup and a virtual function call.
interface ICommand
{
string Name { get; }
void Invoke();
}
//Example commands
class Edit : ICommand
{
string Name { get { return "edit"; } }
void Invoke()
{
//Do whatever you need to do for the edit command
}
}
class Delete : ICommand
{
string Name { get { return "delete"; } }
void Invoke()
{
//Do whatever you need to do for the delete command
}
}
class CommandParser
{
private Dictionary<string, ICommand> commands = new ...;
public void AddCommand(ICommand cmd)
{
commands.Insert(cmd.Name, cmd);
}
public void Parse(string commandLine)
{
string[] args = SplitIntoArguments(commandLine); //Write that method yourself :)
foreach(string arg in args)
{
ICommand cmd = commands.Find(arg);
if (!cmd)
{
throw new SyntaxError(String.Format("{0} is not a valid command.", arg));
}
cmd.Invoke();
}
}
}
class CommandParserXyz : CommandParser
{
CommandParserXyz()
{
AddCommand(new Edit);
AddCommand(new Delete);
}
}
Be aware that you can put attributes on parameters which might make things more readable, e.g.
public void TOPIC (
[ArgInfo("Specify topic ID...")] int Id,
[ArgInfo("Specify topic page...")] int? page)
{
...
}
I can see two different problems here:
Resolving method name (as string) to command module
You could use Dictionary to map string to method just like in Billy's answer. If you prefer only method over command object, you can map string to method directly in C#.
static Dictionary<string, Action<List<string>>> commandMapper;
static void Main(string[] args)
{
InitMapper();
Invoke("TOPIC", new string[]{"1","2","3"}.ToList());
Invoke("Topic", new string[] { "1", "2", "3" }.ToList());
Invoke("Browse", new string[] { "1", "2", "3" }.ToList());
Invoke("BadCommand", new string[] { "1", "2", "3" }.ToList());
}
private static void Invoke(string command, List<string> args)
{
command = command.ToLower();
if (commandMapper.ContainsKey(command))
{
// Execute the method
commandMapper[command](args);
}
else
{
// Command not found
Console.WriteLine("{0} : Command not found!", command);
}
}
private static void InitMapper()
{
// Add more command to the mapper here as you have more
commandMapper = new Dictionary<string, Action<List<string>>>();
commandMapper.Add("topic", Topic);
commandMapper.Add("browse", Browse);
}
static void Topic(List<string> args)
{
// ..
Console.WriteLine("Executing Topic");
}
static void Browse(List<string> args)
{
// ..
Console.WriteLine("Executing Browse");
}
Command-line arguments parsing
People have been scratching their heads solving this problem in early days ..
But now we has library that specifically handle this problem. See http://tirania.org/blog/archive/2008/Oct-14.html or NDesk.Options. This should be easier and could handle some pitfall cases than rolling out new one.
UPDATE: I made major changes to this post - check the revision history for details.
I'm starting to dive into TDD with NUnit and despite I've enjoyed checking some resources I've found here at stackoverflow, I often find myself not gaining good traction.
So what I'm really trying to achieve is to acquire some sort of checklist/workflow —and here's where I need you guys to help me out— or "Test Plan" that will give me decent Code Coverage.
So let's assume an ideal scenario where we could start a project from scratch with let's say a Mailer helper class that would have the following code:
(I've created the class just for the sake of aiding the question with a code sample so any criticism or advice is encouraged and will be very welcome)
Mailer.cs
using System.Net.Mail;
using System;
namespace Dotnet.Samples.NUnit
{
public class Mailer
{
readonly string from;
public string From { get { return from; } }
readonly string to;
public string To { get { return to; } }
readonly string subject;
public string Subject { get { return subject; } }
readonly string cc;
public string Cc { get { return cc; } }
readonly string bcc;
public string BCc { get { return bcc; } }
readonly string body;
public string Body { get { return body; } }
readonly string smtpHost;
public string SmtpHost { get { return smtpHost; } }
readonly string attachment;
public string Attachment { get { return Attachment; } }
public Mailer(string from = null, string to = null, string body = null, string subject = null, string cc = null, string bcc = null, string smtpHost = "localhost", string attachment = null)
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
this.cc = cc;
this.bcc = bcc;
this.smtpHost = smtpHost;
this.attachment = attachment;
}
public void SendMail()
{
if (string.IsNullOrEmpty(From))
throw new ArgumentNullException("Sender e-mail address cannot be null or empty.", from);
SmtpClient smtp = new SmtpClient();
MailMessage mail = new MailMessage();
smtp.Send(mail);
}
}
}
MailerTests.cs
using System;
using NUnit.Framework;
using FluentAssertions;
namespace Dotnet.Samples.NUnit
{
[TestFixture]
public class MailerTests
{
[Test, Ignore("No longer needed as the required code to pass has been already implemented.")]
public void SendMail_FromArgumentIsNotNullOrEmpty_ReturnsTrue()
{
// Arrange
dynamic argument = null;
// Act
Mailer mailer = new Mailer(from: argument);
// Assert
Assert.IsNotNullOrEmpty(mailer.From, "Parameter cannot be null or empty.");
}
[Test]
public void SendMail_FromArgumentIsNullOrEmpty_ThrowsException()
{
// Arrange
dynamic argument = null;
Mailer mailer = new Mailer(from: argument);
// Act
Action act = () => mailer.SendMail();
act.ShouldThrow<ArgumentNullException>();
// Assert
Assert.Throws<ArgumentNullException>(new TestDelegate(act));
}
[Test]
public void SendMail_FromArgumentIsOfTypeString_ReturnsTrue()
{
// Arrange
dynamic argument = String.Empty;
// Act
Mailer mailer = new Mailer(from: argument);
// Assert
mailer.From.Should().Be(argument, "Parameter should be of type string.");
}
// INFO: At this first 'iteration' I've almost covered the first argument of the method so logically this sample is nowhere near completed.
// TODO: Create a test that will eventually require the implementation of a method to validate a well-formed email address.
// TODO: Create as much tests as needed to give the remaining parameters good code coverage.
}
}
So after having my first 2 failing tests the next obvious step would be implementing the functionality to make them pass, but, should I keep the failing tests and create new ones after implementing the code that will make those pass, or should I modify the existing ones after making them pass?
Any advice about this topic will really be enormously appreciated.
If you install TestDriven.net, one of the components (called NCover) actually helps you understand how much of your code is covered by unit test.
Barring that, the best solution is to check each line, and run each test to make sure you've at least hit that line once.
I'd suggest that you pick up some tool like NCover which can hook onto your test cases to give code coverage stats. There is also a community edition of NCover if you don't want the licensed version.
If you use a framework like NUnit, there are methods available such as AssertThrows where you can assert that a method throws the required exception given the input: http://www.nunit.org/index.php?p=assertThrows&r=2.5
Basically, verifying expected behavior given good and bad inputs is the best place to start.
When people (finally!) decide to apply test coverage to an existing code base, it is impractical to test everything; you don't have the resources, and there isn't often a lot of real value.
What you ideally want to do is to make sure that your tests apply to newly written/modified code and anything that might be affected by those changes.
To do this, you need to know:
what code you changed. Your source control system will help you here at the level of this-file-changed.
what code is executed as a consequence of the new code being executed. For this you need either a static analyzer that can trace the downstream impact of the code (don't know of many of these) or a test coverage tool, which can show what has been executed when you run your specific tests. Any such executed code probably needs re-testing, too.
Because you want to minimize the the amount of test code you write, you clearly want better than file-precision granularity of "changed". You can use a diff tool (often build into your source control system) to help hone the focus to specific lines. Diff tools don't actually understand code structure, so what they report tends to be line-oriented rather than structure oriented, producing rather bigger diffs than necessary; nor do they tell you the convenient point of test access, which is likely to be a method because the whole style of unit test is focused on testing methods.
You can get better diff tools. Our Smart Differencer tools provide differences in terms of program structures (expressions, statements, methods) and abstracting editing operations (insert, delete, copy, move, replace, rename) which can make it easier to interpret the code changes. This doesn't directly solve the "which method changed?" question, but it often means looking at a lot less stuff to make that decision.
You can get test coverage tools that will answer this question. Our Test Coverage tools have a facility to compare previous test coverage runs with current test coverage runs, to tell you which tests have to be re-run. They do so by examining the code differences (something like the Smart Differencer) but abstract the changes back to method level.
Suppose I am watching something in VS2008 and I want to search the object I'm watching for an instance of a particular value.
For example, say I'm watching this and I want to search for a control called mycontrol inside it or I want to search for a string that currently has the value of "Hello World".
I know it's kind of a brute force way of doing things, but I find it would be a quick way of identifying where things are going wrong whilst debugging. Warning: I'm about to swear... When I was using PHP, I could see all variables that were currently set by using get_defined_vars(). It was dirty, but it was quick and I'd like to know if there's something similar I can do in VS.
Sorry if I've been a little vague, I'd be happy to elaborate if you have questions.
Cheers
Iain
Edit:
What I'm actually tring to do is interrogate the current state of the application and quickly search for the various classes that I want to debug. What I'm doing is trying to debug where I don't have the source code (I'm writing a control for a CMS). So I know what the value of something should be, but I don't know where in the structure it exists - that's what I want to find.
e.g. An exeption is thrown by the application because foo should be a list of the type bar. I want to find out where foo is defined so I can look around and see what the other variables in the same class are set to.
Sorry again, I'm finding it hard to explain :(
Edit #2:
I find a good tree might help me visualise it better...
Quickwatch
-this
-var1
+var1a
+var1b
-var1c
-base
-foo = "Hello World"
+var1ca
+var2
+var3
In the above, how would I quickly drill down through the structure to find foo?
It sounds like you want a conditional breakpoint:
When the breakpoint location is
reached, the expression is evaluated
and the breakpoint is hit only if the
expression is true or has changed.
Create a new breakpoint, right-click on it, and select "Condition..." Enter the condition you'd like to wait for. It'll be something like:
this.MyString == "Hello World"
EDIT: Ok, I understand now you want to interrogate another, running application. Assuming it was built in a managed language, you may be interested in Hawkeye:
Hawkeye is the only .Net tool that
allows you to view, edit, analyze and
invoke (almost) any object from a .Net
application. Whenever you try to
debug, test, change or understand an
application, Hawkeye can help.
Free. Not been updated in a while.
I wrote this the other day. It did the job well enough (however it is only some utility code for debugging, so use at your own risk --> the design is pretty bad >_< ). Dumps out the fields and iterates downwards. It might not be perfect, but it solved my problem at the time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
public static class Dumper
{
public class Dump
{
public Dump(bool spacesInsteadOfTab)
{
_spacesIndeadOfTab = spacesInsteadOfTab;
}
private readonly StringBuilder _sb = new StringBuilder();
public string Result
{
get
{
return _sb.ToString();
}
}
private readonly bool _spacesIndeadOfTab;
private int _currentIndent;
public int CurrentIndent
{
get
{
return _currentIndent;
}
set
{
_currentIndent = value > 0 ? value : 0;
}
}
public void IncrementIndent()
{
CurrentIndent += 1;
}
public void DecrementIndent()
{
CurrentIndent -= 1;
}
private void AppendIndent()
{
if (_spacesIndeadOfTab)
_sb.Append(' ', _currentIndent * 4);
else
_sb.Append('\t', _currentIndent);
}
public void Log(string logValue)
{
AppendIndent();
_sb.AppendLine(logValue);
}
public void Log(string logValue, params object[] args)
{
AppendIndent();
_sb.AppendFormat(logValue, args);
_sb.AppendLine();
}
}
public static Dump TakeDump(object objectToDump, int maxDepth)
{
Dump result = new Dump(false);
int currentDepth = 0;
TakeDump(ref result, ref currentDepth, maxDepth, objectToDump);
return result;
}
private static void TakeDump(ref Dump result, ref int currentDepth, int maxDepth, object objectToDump)
{
currentDepth++;
if (currentDepth > maxDepth)
{
result.IncrementIndent();
result.Log("MaxDepth ({0}) Reached.", maxDepth);
result.DecrementIndent();
return;
}
var objectType = objectToDump.GetType();
result.Log("--> {0}", objectType.FullName);
result.IncrementIndent();
var fields = objectType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (fields.Count() == 0)
result.Log("No fields");
foreach (var fieldInfo in fields)
{
var fieldValue = fieldInfo.GetValue(objectToDump);
if (fieldValue == null)
result.Log("{0} is null", fieldValueType.FullName, fieldInfo.Name);
var fieldValueType = fieldValue.GetType();
if (fieldValueType.IsValueType)
result.Log("{2} as {0} (ToString: {1})", fieldValueType.FullName, fieldValue.ToString(), fieldInfo.Name);
else
TakeDump(ref result, ref currentDepth, maxDepth, fieldValue);
}
result.DecrementIndent();
}
}
It sounds like you are envisioning a feature which would descend down the tree presented in the debugger UI looking for a typed in value. This is not a feature of the debugger at this time (although at first glance it sounds handy). It would have problems though in cases where the expression had infinite expansions.
Circular references for instance will cause an infinite expansion. Those are fairly easy to track down but there are more nefarious tricks which can be done to make infinite expansion harder / impossible to track. True we could probably control for depth and such ...
I think your best bet is to write a reflection based searching mechanism with a depth control mechanism. Then call this API from the debugger window.