Related
I've a C# big solution that contains different projects. It contains also a batch with a Main static method.
I have to identify and remove all the classes that can't be used starting from this method.
Which is the best way to do that?
I'm using Microsoft Visual Studio Professional 2015
Thanks!
There are no tools which can do this completely, because
System.Reflection and System.CodeDom exist - Is it possible to dynamically compile and execute C# code fragments?
New C# code can be generated at run-time, which uses otherwise-unused classes.
No tools can predict what that new C# code is (apart from the humans who wrote the code)
Dependency Injection libraries (which use System.Reflection behind the scenes) can call "unused" classes. This happens frequently with MVC Controller classes.
Razor Views can use classes. These are not compiled by default. Instead, they will crash at runtime if a class is missing.
Assuming no-one is using System.Reflection, you could do it by hand.
For each class:
Select it in Visual Studio, right-click then "Find All References"
If none found, comment the class out /* */
Rebuild all (including Razor views). If no errors found, then the class is unused.
It might be worth downloading and starting the free trial of resharper. By using the "Solution-Wide Analysis" feature, you will be able to quickly find code that is not used anywhere in the solution. Plus there are loads of other cool features too!
You can try by right click and check the references. It is possible for methods, classes and properties as well . But it won't show if the class, method or property referenced in presentation layer
The below code can be used with the FxCopCmd.exe command line executable. FxCopCmd.exe comes part of the default Visual Studio installation. First you have to setup a new project and compile the code into a DLL.
In Visual Studio, create a new Class Library project. Pick a name like FxCopDemo. It must target .NET Framework 4 or newer. During compilation, a warning message will be displayed about the build. The warning message can be fixed by changing Properties > Build > Platform target to x86.
In the project, add references to:
FxCopSdk.dll
Microsoft.Cci.dll
Depending on your Visual Studio version, the DLLs are located in a folder such as:
C:\Program Files (x86)\Microsoft Visual Studio 11.0\Team Tools\Static Analysis Tools\FxCop\ or
C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\Rules\
Make sure to do a Debug build so that the program database (PDB) files are generated. The PDB files will supply FxCop with the debugging information it needs output the line numbers from the source code.
Copy and paste this XML into a file named UnusedCodeXml.xml and add it to the project as an existing item.
<?xml version="1.0" encoding="utf-8"?>
<Rules FriendlyName="">
<Rule TypeName="UnusedClassesRule" Category="UnusedCode" CheckId="UC0001">
<Name>A class or class cluster is not referenced by the main code.</Name>
<Description>
A class is included as part of an executable, or class library, but that code is not used.
</Description>
<Url></Url>
<Resolution Name="Orphan">The class '{0}' is not referenced by any other classes.</Resolution>
<Resolution Name="Cluster">The class '{0}' is only referenced by classes in an isolated cluster.</Resolution>
<MessageLevel Certainty="95">Warning</MessageLevel>
<Email></Email>
<FixCategories>NonBreaking</FixCategories>
<Owner></Owner>
</Rule>
</Rules>
Important: After adding the XML rule file to the project. Then right-click on the item, goto Properties, and change the Build Action to Embedded Resource.
Note: The C# code is just a little too long to fit in this post together, so the code is posted below. Copy and paste the C# code in my other answer into a file named UnusedClassesRule.cs. Add the file to the project. Compiling the project should create a file called C:\Projects\FxCopDemo\bin\Debug\FxCopDemo.dll.
Now open a Command Prompt window, and go to the directory that contains the FxCopCmd.exe executable. FxCopCmd.exe seems a little finicky with the order the arguments are passed in. It seems like /d: has to be specified last when used.
Case 1) Analyze a single executable, i.e. MyProgram.exe. The command line for that would be:
FxCopCmd "/f:C:\Projects\MyProgram\bin\Debug\MyProgram.exe" /r:C:\Projects\FxCopDemo\bin\Debug\FxCopDemo.dll /out:c:\temp\refs.csv /q /c
/f: is the path to the exe to analyze
/r: is the path to the rule dll
/out: optional. saves the output to the specified file
/q means quiet
/c optional. Outputs the results to the console.
/v optional. Verbose. If specified, a list of all references each class has will be included in the output.
Case 2) You have a project that compiles into a DLL and an EXE. In this case, copy the files MyProgram.exe, MyProgram.Core.dll, and PBD files into a separate subfolder, e.g. \bin\Debug\fxcop\. The /f: argument will point to the subfolder.
FxCopCmd /f:C:\Projects\MyProgram\bin\Debug\fxcop\ /r:C:\Projects\FxCopDemo\bin\Debug\FxCopDemo.dll /out:c:\temp\refs.csv /q /c /d:"C:\Projects\MyProgram\bin\Debug\"
In this case, an extra /d: argument is required. It points to the main Debug folder, which will probably contain third party DLLs that you don't want to include in the analysis, but are required by FxCop.
Here is a screenshot of the sample output. It outputs any classes that aren't referred to by other classes, as well as it detects classes that are disconnected from the rest of the code.
Finally, the following document is helpful to troubleshoot any issues with FxCop:
https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.466.282&rep=rep1&type=pdf
Here is the code for the UnusedClassesRule.cs class:
using Microsoft.FxCop.Sdk;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FxCopDemo {
[Flags]
public enum CodeLocation {
StaticInitializer = 4096,
ConstructorParameter = 1,
ConstructorBody = 2,
//---
MethodParameter = 4,
MethodBody = 8,
MethodReturnType = 16,
StaticMethodBody = 2048,
MethodDef = 8192,
ClassDef = 16384,
//---
PropertyReturnType = 32,
PropertyGetterBody = 64,
PropertySetterBody = 128,
//---
EventDelegate = 256,
//---
MemberVariable = 512,
StaticVariable = 1024
}
public class RefItem {
///<summary>All the locations in the source code where the full name is referenced.</summary>
public CodeLocation Locations = (CodeLocation) 0;
///<summary>The full name of the class, struct, or enum.</summary>
public readonly String FullName;
///<summary>The total number of times the FullName is referenced by the ClassTypes.</summary>
public int Count;
public RefItem(String fullName) {
this.FullName = fullName;
}
}
// for each custom class, maintain a collection of all types that the class references
public class Class2 : IComparable<Class2> {
///<summary>List of other class types that have a direct reference to this class. This list is populated at the very end.</summary>
public List<Class2> Nbors = new List<Class2>();
///<summary>The full name of this class, struct, or enum type.</summary>
public readonly String FullName;
///<summary>If this class is a nested class, the DeclaringType is the full name of the immediate host.</summary>
public readonly String DeclaringType;
///<summary>The full name of the immediate parent.</summary>
public readonly String BaseType;
///<summary>True if this class contains the Main(...) entry point method.</summary>
public readonly bool IsEntryPoint;
///<summary>The type of node, typically Class, Interface, DelegateNode, EnumNode. Used for reporting purposes.</summary>
public readonly NodeType NodeType;
private Dictionary<String, RefItem> dict = new Dictionary<String, RefItem>();
public Class2(String fullName, bool isEntryPoint, String declaringType, String baseType, NodeType nodeType) {
this.FullName = fullName;
this.IsEntryPoint = isEntryPoint;
this.DeclaringType = declaringType;
this.BaseType = baseType;
this.NodeType = nodeType;
}
///<summary>
///Adds a reference to a class that this class directly references.
///<param name="fullName">The full name of the class that this class references.</param>
///<param name="loc">The location in the code where the reference occurs. CodeLocation is a [Flags] enum, so multiple locations can exist.</param>
///</summary>
public void Add(String fullName, CodeLocation loc) {
// variables passed with 'ref' or 'out' as method parameters have an '#' as the last character of the fullName
fullName = fullName.TrimEnd('#');
lock(dict) {
RefItem ri = null;
if (!dict.TryGetValue(fullName, out ri)) {
ri = new RefItem(fullName);
dict[fullName] = ri;
}
ri.Locations = (ri.Locations | loc);
ri.Count++;
}
}
public RefItem GetRef(String fullName) {
RefItem ri = null;
dict.TryGetValue(fullName, out ri);
return ri;
}
public List<RefItem> GetRefs() {
return dict.Values.OrderBy(r => r.FullName).ToList();
}
public override String ToString() {
return FullName;
}
public int CompareTo(Class2 other) {
return String.Compare(this.FullName, other.FullName);
}
}
/*
For each type node, detect all other types it references.
Then for each node, determine the other nodes that point to it.
Report:
1. Any nodes that aren't pointed to, and
2. Any groups of nodes that are not connected to the main entry point.
Limitations:
1. Seems like when an enum is cast to a int value, the original enum type is not recoverable
e.g: int x = (int) MyEnum.SomeValue; // seems like the compiler replaces the MyEnum.SomeValue reference with the literal value
This means that some enums may be reported as unused, but they are actually used.
2. Unreachable code is reported as unused. Not sure how to access unreachable code using the FxCop api.
3. Does not account for reflection that uses hard coded string names.
The following are too granular and are not reported in this rule:
1. Methods (instance or static) that are never called.
2. Variables (properties or fields, instance or static) in classes not used.
3. Variables in method arguments not used.
*/
public class UnusedClassesRule : BaseIntrospectionRule {
private static Dictionary<String, Class2> dictTypes = new Dictionary<String, Class2>();
// The multi-threads in FxCop make it hard to debug. The messages are grouped by thread when debugging of this rule is needed.
private static Dictionary<int, StringBuilder> dictOutput = new Dictionary<int, StringBuilder>();
private Class2 currentC2 = null;
private CodeLocation currentLoc = (CodeLocation) 0;
private Member currentMember = null;
private bool writeCheckMemberError = true;
private bool writeVisitExpressionError = true;
public UnusedClassesRule() : base("UnusedClassesRule", "FxCopDemo.UnusedCodeXml", typeof(UnusedClassesRule).Assembly) {
}
public override TargetVisibilities TargetVisibility {
get {
return TargetVisibilities.All;
}
}
// Note: BeforeAnalysis() and AfterAnalysis() are called multiple times, one for each rule instance.
// According to a forum post on MSDN, one rule instance is created for each CPU core.
//public override void BeforeAnalysis() {
//base.BeforeAnalysis();
//MethodCollection list = CallGraph.CallersFor((Method) null);
//}
public override void AfterAnalysis() {
base.AfterAnalysis();
lock(dictTypes) {
if (dictTypes.Count == 0)
return;
try {
FinalAnalysis();
} catch (Exception ex) {
writeCheckMemberError = false;
Write("Error: " + ex.GetType().FullName + ": " + ex.Message, null, true);
Write(ex.StackTrace, null, true);
}
dictTypes.Clear();
}
}
public override ProblemCollection Check(Member member) {
try {
CheckInternal(member);
} catch (Exception ex) {
if (writeCheckMemberError) {
writeCheckMemberError = false;
Write("Error: " + ex.GetType().FullName + ": " + ex.Message);
Write(ex.StackTrace);
}
}
return this.Problems;
}
private ProblemCollection CheckInternal(Member member) {
Class2 ct = currentC2;
AttributeNodeCollection attribs = member.Attributes;
for (int i = 0; i < attribs.Count; i++) {
AttributeNode a = attribs[i];
Add(ct, a.Type, CodeLocation.MethodDef);
}
if (member is Method) {
currentMember = member;
var m = (Method) member;
if (m.ReturnType != FrameworkTypes.Void)
Add(ct, m.ReturnType, CodeLocation.MethodReturnType);
if (m.NodeType == NodeType.InstanceInitializer)
currentLoc = CodeLocation.ConstructorBody;
else if (m.NodeType == NodeType.StaticInitializer)
currentLoc = CodeLocation.StaticInitializer;
else
currentLoc = (m.IsStatic ? CodeLocation.StaticMethodBody : CodeLocation.MethodBody);
for (int i = 0; i < m.Parameters.Count; i++) {
Parameter p = m.Parameters[i];
TypeNode ty = p.Type;
Add(ct, ty, CodeLocation.MethodParameter);
}
// the DeclaringType is needed when an extension method is called
Add(ct, m.DeclaringType, currentLoc);
VisitMethod(m);
//VisitBlock(m.Body);
//VisitStatements(m.Body.Statements);
}
else if (member is PropertyNode) {
var pn = (PropertyNode) member;
if (pn.Setter != null) {
currentMember = member;
currentLoc = CodeLocation.PropertySetterBody;
VisitStatements(pn.Setter.Body.Statements);
}
if (pn.Getter != null) {
Add(ct, pn.Getter.ReturnType, CodeLocation.PropertyReturnType);
currentMember = member;
currentLoc = CodeLocation.PropertyGetterBody;
VisitStatements(pn.Getter.Body.Statements);
}
}
else if (member is Field) {
var f = (Field) member;
var loc = (f.IsStatic ? CodeLocation.StaticVariable : CodeLocation.MemberVariable);
Add(ct, f.Type, loc);
}
else if (member is EventNode) {
var evt = (EventNode) member;
Add(ct, evt.HandlerType, CodeLocation.EventDelegate);
}
else {
Write("UNKNOWN CHECK MEMBER: " + member.GetType().FullName + " full name: " + member.FullName);
}
return this.Problems;
}
private static void Add(Class2 ct, TypeNode ty, CodeLocation loc) {
// Given a TypeNode, it may have to be drilled down into determine it's actual type
// It's also possible that a single TypeNode results in multiple references. For example,
// public class MyList<T> { ... }
// public static void Foo<U>(MyList<U> list) where U : Bar {}
// which should result in adding namespace.MyList, and namespace.Bar
Stack<TypeNode> stack = new Stack<TypeNode>();
stack.Push(ty);
while (stack.Count > 0) {
ty = stack.Pop();
if (ty == null)
continue;
if (ty.IsGeneric) {
// otherwise Nullable`1 shows up in the list of references
if (ty.Template != null && ty.Template != FrameworkTypes.GenericNullable)
ct.Add(ty.Template.FullName, loc);
if (ty.TemplateArguments != null) {
foreach (TypeNode ta in ty.TemplateArguments)
stack.Push(ta);
}
if (ty.TemplateParameters != null) {
foreach (TypeNode tp in ty.TemplateParameters)
stack.Push(tp);
}
}
else if (ty.IsTemplateParameter) {
// this is the case when a constraint is placed on a generic type, e.g. public static void Foo<T>(U input) where U : Bar { ... }
// ty.FullName will be something like "parameter.T"
// The BaseType == "Bar". If no constraint is specified, then BaseType == "System.Object".
// The reason it's pushed on the stack is because it could be a constraint like where : U : MyList<Bar> { ... }
stack.Push(ty.BaseType);
}
else if (ty is ArrayType) {
ArrayType ty2 = (ArrayType) ty;
TypeNode ty3 = ty2.ElementType;
ct.Add(ty3.FullName, loc);
}
else {
ct.Add(ty.FullName, loc);
}
}
}
public override void VisitExpression(Expression exp) {
try {
VisitExpressionInternal(exp);
} catch (Exception ex) {
if (writeVisitExpressionError) {
writeVisitExpressionError = false; // only record the first error
Write("Error: " + ex.GetType().FullName + ": " + ex.Message, null, true);
Write(ex.StackTrace);
}
}
}
private void VisitExpressionInternal(Expression exp) {
if (exp == null)
return;
Stack<Expression> stack = new Stack<Expression>();
stack.Push(exp);
while (stack.Count > 0) {
exp = stack.Pop();
if (exp == null)
continue;
if (exp is NaryExpression) {
// this handles Construct, ConstructArray, MethodCall, Indexer
var ne = (NaryExpression) exp;
foreach (Expression e in ne.Operands) {
stack.Push(e); // the actual arguments
}
}
TypeNode ty = exp.Type;
bool alert = false;
if (exp is Variable || exp is Local) { // Note: Local extends Variable
Add(currentC2, ty, currentLoc);
//Write("VARIABLE NAME: " + ((Variable) exp).Name.Name + " " + exp);
}
else if (exp is Literal) {
var lit = (Literal) exp;
Add(currentC2, ty, currentLoc);
Object val = lit.Value;
if (val is TypeNode)
Add(currentC2, (TypeNode) val, currentLoc);
else if (val != null) {
// val == null when there is an assignment like, MyClass c = null;
Type ty2 = val.GetType();
bool isKnown = (ty2.IsPrimitive || ty2 == typeof(String));
if (isKnown) {
currentC2.Add(ty2.FullName, currentLoc);
}
else {
Write("val.GetType(): " + ty2.FullName);
alert = true;
}
}
//if (val is EnumNode) {
// var en = (EnumNode) val;
// en.UnderlyingType; // this is the primitive type that the Enum extends, typically int32
//}
}
else if (exp is MethodCall) {
var mc = (MethodCall) exp;
stack.Push(mc.Callee);
}
else if (exp is Construct) {
Add(currentC2, ty, currentLoc);
var c = (Construct) exp;
stack.Push(c.Constructor); // c.Constuctor is MemberBinding
}
else if (exp is Indexer) {
var ix = (Indexer) exp;
Add(currentC2, ix.ElementType, currentLoc);
stack.Push(ix.Object);
}
else if (exp is AddressDereference) {
var ad = (AddressDereference) exp; // nullable type
TypeNode ty2 = ad.Type;
Add(currentC2, ty2, currentLoc);
stack.Push(ad.Address);
}
else if (exp is MemberBinding) {
var mb = (MemberBinding) exp;
stack.Push(mb.TargetObject);
if (mb.BoundMember is Method) { // Note: InstanceInitializer is a Method
var m = (Method) mb.BoundMember;
Add(currentC2, m.DeclaringType, currentLoc);
CodeLocation loc = (mb.BoundMember is InstanceInitializer ? CodeLocation.ConstructorParameter : currentLoc);
for (int i = 0; i < m.Parameters.Count; i++) {
Parameter p = m.Parameters[i];
TypeNode ty2 = p.Type;
Add(currentC2, ty2, loc);
}
}
else if (mb.BoundMember is Field) {
// this occurs for a class level static variable
var f = (Field) mb.BoundMember;
var loc = (f.IsStatic ? CodeLocation.StaticVariable : CodeLocation.MemberVariable);
Add(currentC2, f.Type, loc);
Add(currentC2, f.DeclaringType, loc);
}
//else if (mb.BoundMember is PropertyNode) { // can this happen?
//}
else {
alert = true;
}
}
else if (exp is ConstructArray) {
var ca = (ConstructArray) exp;
Add(currentC2, ca.ElementType, currentLoc);
}
else if (exp is UnaryExpression) {
// e.g. a method call
var ue = (UnaryExpression) exp;
stack.Push(ue.Operand);
}
else if (exp is BinaryExpression) {
var be = (BinaryExpression) exp;
stack.Push(be.Operand1);
stack.Push(be.Operand2);
}
else if (exp is TernaryExpression) {
var te = (TernaryExpression) exp;
stack.Push(te.Operand1);
stack.Push(te.Operand2);
stack.Push(te.Operand3);
}
else if (exp is NamedArgument) {
var na = (NamedArgument) exp;
stack.Push(na.Value);
}
else {
NodeType nt = exp.NodeType;
bool ignore = (nt == NodeType.Pop || nt == NodeType.Dup);
alert = !ignore;
}
if (alert)
Write("x x " + currentC2.FullName + " " + currentMember.FullName + " " + exp.ToString() + " NodeType:" + exp.NodeType + " Type:" + exp.Type);
}
}
public override ProblemCollection Check(ModuleNode module) {
//Write("Check_ModuleNode: " + module.Name);
return this.Problems;
}
public override ProblemCollection Check(Parameter parameter) {
//Write("Check_Parameter: " + parameter.Name);
return this.Problems;
}
public override ProblemCollection Check(Resource resource) {
//Write("Check_Resource: " + resource.Name);
return this.Problems;
}
public override ProblemCollection Check(TypeNode type) {
//Write("Check_TypeNode: " + type.FullName + " declaring type: " + (type.DeclaringType == null ? "null" : type.DeclaringType.FullName) + " " + type.Interfaces);
currentC2 = GetClass2(type.FullName, type);
base.Check(type);
AttributeNodeCollection attribs = type.Attributes;
//Write("attributs.COunt: " + attribs.Count);
for (int i = 0; i < attribs.Count; i++) {
AttributeNode a = attribs[i];
//Write("a.Type: " + a.Type);
Add(currentC2, a.Type, CodeLocation.ClassDef);
}
InterfaceCollection iList = type.Interfaces;
for (int i = 0; i < iList.Count; i++) {
InterfaceNode n = iList[i];
Add(currentC2, n, CodeLocation.ClassDef);
}
return this.Problems;
}
private static Class2 GetClass2(String fullName, TypeNode ty) {
lock(dictTypes) {
Class2 ct = null;
if (!dictTypes.TryGetValue(fullName, out ct)) {
AssemblyNode a = ty.ContainingAssembly();
bool isEntryPoint = false;
Method ep = a.EntryPoint;
if (ep != null)
isEntryPoint = (String.Compare(fullName, ep.DeclaringType.FullName) == 0);
TypeNode dt = ty.DeclaringType;
TypeNode bt = ty.BaseType;
String dt_ = (dt == null ? "" : dt.FullName);
String bt_ = (bt == null ? "" : bt.FullName);
//Write("type: " + ty.FullName + " nt:" + ty.NodeType + " dt:" + dt_ + " bt:" + bt_);
ct = new Class2(fullName, isEntryPoint, dt_, bt_, ty.NodeType);
dictTypes[fullName] = ct;
}
return ct;
}
}
public override ProblemCollection Check(String namespaceName, TypeNodeCollection types) {
//Write("Check_namespaceName_types: " + namespaceName + ", " + types.Count);
return this.Problems;
}
protected override String GetLocalizedString(String name, params Object[] arguments) {
String s = base.GetLocalizedString(name, arguments);
//Write("GetLocalizedString: " + name);
return s;
}
protected override Resolution GetNamedResolution(String name, params Object[] arguments) {
Resolution r = base.GetNamedResolution(name, arguments);
//Write("GetNamedResolution: " + name);
return r;
}
private static void Write(String message, StreamWriter sw = null, bool console = false) {
if (dictOutput == null) {
if (console)
Console.WriteLine(message);
if (sw != null)
sw.WriteLine(message);
return;
}
lock(dictOutput) {
int id = System.Threading.Thread.CurrentThread.ManagedThreadId;
StringBuilder sb = null;
if (!dictOutput.TryGetValue(id, out sb)) {
sb = new StringBuilder();
dictOutput[id] = sb;
}
sb.AppendLine(message);
}
}
// chops off "Node" at the end, e.g: EnumNode > Node, DelegateNode > Delegate
private static String GetName(NodeType nt) {
String s = nt.ToString();
if (s.EndsWith("Node"))
s = s.Substring(0, s.Length - 4);
return s;
}
private static void FinalAnalysis() {
foreach (KeyValuePair<int, StringBuilder> pair in dictOutput) {
Console.WriteLine();
Console.WriteLine("Thread Id: " + pair.Key);
Console.Write(pair.Value);
}
dictOutput = null;
bool writeRefs = false;
bool console = false;
String outputFilename = null;
StreamWriter sw2 = null;
String[] args = Environment.GetCommandLineArgs(); // look at the args to determine what to output
foreach (String arg in args) {
if (arg.StartsWith("/out:")) {
outputFilename = arg.Substring(5);
String folder = Path.GetDirectoryName(outputFilename);
if (!Directory.Exists(folder))
Directory.CreateDirectory(folder);
System.Threading.Thread.Sleep(2000);
sw2 = new StreamWriter(new FileStream(outputFilename, FileMode.Create, FileAccess.Write));
}
else if (arg == "/c" || arg == "/console")
console = true;
else if (arg == "/v" || arg == "/verbose")
writeRefs = true;
}
Write("", sw2, console);
List<Class2> ctList = dictTypes.Values.OrderBy(t => t.FullName).ToList();
Hashtable htCluster = new Hashtable();
Hashtable ht = new Hashtable();
foreach (Class2 ct in ctList)
ht[ct.FullName] = ct;
foreach (Class2 ct in ctList) {
if (ct.DeclaringType.Length == 0) {
List<Class2> list = new List<Class2>();
list.Add(ct);
htCluster[ct] = list;
}
}
foreach (Class2 ct in ctList) {
if (ct.DeclaringType.Length == 0)
continue;
var ct2 = ct;
while (ct2.DeclaringType.Length > 0)
ct2 = (Class2) ht[ct2.DeclaringType];
List<Class2> list = (List<Class2>) htCluster[ct2];
ct2 = ct;
while (ct2.DeclaringType.Length > 0) {
list.Add(ct2);
ct2 = (Class2) ht[ct2.DeclaringType];
}
htCluster[ct] = list;
}
if (writeRefs) {
Write("ClassName,RefersTo,Locations", sw2, console);
foreach (Class2 ct in ctList) {
List<RefItem> refList = ct.GetRefs();
foreach (RefItem ri in refList)
Write(ct.FullName + "," + ri.FullName + "," + ri.Locations.ToString().Replace(',', ' '), sw2, console);
}
}
// if a nested class points to a parent class, don't count that as a reference
// if the only reference to class A is the construtor body of class B and class B extends class A, then don't count that as a reference
int n = ctList.Count;
for (int i = 0; i < n; i++) {
// check for other classes (ct2) that refer to ct
for (int j = 0; j < n; j++) {
if (i == j)
continue;
Class2 ct = ctList[i];
Class2 ct2 = ctList[j];
bool isNested = false;
String dt = ct2.DeclaringType;
while (dt.Length > 0) {
var p = (Class2) ht[dt];
if (p == ct) {
isNested = true;
break;
}
dt = p.DeclaringType;
}
if (isNested) {
//Write("Skipping: " + ct2.FullName + " because it is a nested class of: " + ct.FullName);
continue;
}
RefItem ri = ct2.GetRef(ct.FullName);
if (ri == null)
continue; // no references exist
if (ri.Locations == CodeLocation.ConstructorBody) {
bool isDescendant = false;
// ConstructorBody is the only location typically because of the implicit "base()" call.
// Check if ct2 is a descendant class of ct. If so, don't count the reference.
String bt = ct2.BaseType;
while (bt.Length > 0) {
var p = (Class2) ht[bt];
if (p == null)
break;
if (p == ct) {
isDescendant = true;
break;
}
bt = p.BaseType;
}
if (isDescendant)
continue; // skip, does not count as a reference
}
var list1 = (List<Class2>) htCluster[ct];
var list2 = (List<Class2>) htCluster[ct2];
if (list1 != list2) {
// group the nodes as part of the same cluster
List<Class2> list3 = new List<Class2>();
list3.AddRange(list1);
list3.AddRange(list2);
foreach (Class2 c2 in list3)
htCluster[c2] = list3;
//Write("Merged: [" + String.Join(", ", list1.Select(t => t.FullName)) + "] and [" + String.Join(", ", list2.Select(t => t.FullName)) + "]", sw2, console);
}
while (ct != null) {
if (ct2 != ct)
ct.Nbors.Add(ct2);
//Console.WriteLine(ct.FullName + ".Nbors.Add(" + ct2.FullName + ")");
ct = (Class2) ht[ct.DeclaringType];
}
}
}
// for each type that has a non-ConstructorBody reference, the ancestor types are counted as references.
// for example, if there were 5 classes A to E, where each one extends the one before it, i.e: A < B < C < D < E
// Suppose only class C is referenced somewhere in the code, then classes A & B are considered to be used,
// but classes D & E are considered unused.
foreach (Class2 ct in ctList) {
if (ct.Nbors.Count == 0)
continue;
Class2 ct2 = ct;
String bt = ct2.BaseType;
while (bt.Length > 0) {
var p = (Class2) ht[bt];
if (p == null)
break;
if (p.Nbors.Count == 0)
p.Nbors.Add(ct2);
bt = p.BaseType;
ct2 = p;
}
}
List<List<Class2>> clusters = htCluster.Values.Cast<List<Class2>>().Distinct().ToList();
List<List<Class2>> clusters2 = new List<List<Class2>>();
foreach (List<Class2> list in clusters) {
if (list.FirstOrDefault(t => t.IsEntryPoint) != null)
continue; // the cluster that contains the Main() method is not reported as an isolated cluster
// a cluster must have two or more non-related, non-nested classes
var list2 = list.Where(t => t.DeclaringType.Length == 0).ToList();
if (list2.Count > 1)
clusters2.Add(list2);
}
if (clusters2.Count > 0) {
Write(clusters2.Count + " isolated code clusters detected:", sw2, console);
for (int i = 0; i < clusters2.Count; i++) {
List<Class2> list = clusters2[i];
list.Sort();
Write("Cluster " + (i+1) + " has " + list.Count + " classes:", sw2, console);
for (int j = 0; j < list.Count; j++) {
Write(" " + (j+1) + ". " + list[j].FullName + " (" + GetName(list[j].NodeType) + ")", sw2, console);
}
Write("", sw2, console);
}
}
// the class that contains the entry point is not reported as unused
// Don't report anonymous classes (classes that have "<>" in the name)
List<Class2> ctUnused = ctList.Where(t => t.Nbors.Count == 0 && t.FullName.IndexOf("<>") < 0 && !t.IsEntryPoint).ToList();
if (ctUnused.Count > 0) {
// group by NodeType
Dictionary<NodeType, List<Class2>> dict = new Dictionary<NodeType, List<Class2>>();
foreach (Class2 ct in ctUnused) {
Class2 ct2 = ct;
bool include = true;
while (ct2.DeclaringType.Length > 0) {
var dt = (Class2) ht[ct2.DeclaringType];
if (dt.Nbors.Count == 0) {
// don't report a nested class if a parent class will be reported
include = false;
break;
}
ct2 = dt;
}
if (!include)
continue;
List<Class2> list = null;
if (!dict.TryGetValue(ct.NodeType, out list)) {
list = new List<Class2>();
dict[ct.NodeType] = list;
}
list.Add(ct);
}
List<NodeType> types = dict.Keys.ToList();
types.Sort();
foreach (NodeType key in types) {
String keyName = GetName(key);
List<Class2> list = dict[key];
Write("", sw2, console);
if (list.Count > 1)
Write("The following " + list.Count + " " + keyName + "s are not used by other code:", sw2, console);
else
Write("The following " + keyName + " is not used by other code:", sw2, console);
for (int i = 0; i < list.Count; i++) {
Write((i + 1) + ": " + list[i].FullName, sw2, console);
}
}
}
if (sw2 != null) {
sw2.Flush();
sw2.Dispose();
}
}
}
}
As per my understanding of the question , you are opting to find classes that cannot be reached from main method that is in the batch.
As per the layout of the solution, all the classes can be reached from the main method project by adding references and by making use of the using keyword.External references to other solution projects can be done by adding dll and then importing the namespace.
And if you want to perform clean up and remove unused classed from your solutions, you need to carefully check direct and indirect references of the classes.
Think the above answers your question, please mark as answer if you are satisfied.
Try reading the .sln file. It will contain references to all the projects used.
For each project used, open the .csproj file. All files under compile are used for project building. You can safely remove other files assuming you have one class per file.
In my syntax below I get an error of
An exception of type 'System.FormatException' occured in mscorlib.dll but was not handled in ther user code
Additional Information: Input string was not in a correct format
And the value being passed that throws the error is 9.7000
And this is my syntax - I have a comment above the line that throws an error
private void calculate()
{
var qtys = val(dropdownlist.SelectedItem.Text) + "|" + val(dropdownlist1.SelectedItem.Text) + "|" + val(dropdownlist2.SelectedItem.Text) ;
var totalitems = dropdownitem.SelectedItem.Text + "|" + dropdownitem1.SelectedItem.Text + "|" + dropdownitem2.SelectedItem.Text + "|" + dropdownitem3.SelectedItem.Text;
var amounts = dropdownamt.SelectedItem.Value + "|" + dropdownamt1.SelectedItem.Value + "|" + dropdownamt2.SelectedItem.Value + "|" + dropdownamt3.SelectedItem.Value;
var totalitems = itemInfo.Split('|');
var qtys = qty.Split('|');
var amounts = amount.Split('|');
for (int i = 0; i < totalitems.Count(); i++)
{
if (totalitems[i] != "" && qtys[i] != "" && qtys[i] != "0")
{
TotalPrice += Math.Round(val(amounts[i]), 2);
TotalTax += Math.Round(val(amounts[i]) * Convert.ToDecimal(0.07), 2);
}
}
}
private decimal val(string p)
{
if (p == null)
return 0;
else if (p == "")
return 0;
//The value of p = 9.7000
return Convert.ToInt16(p);
}
You're trying to return a decimal but calling Convert.ToInt16 try Convert.ToDecimal
Also I would recommend using decimal.TryParse rather than calling Convert directly with some code like this:
public decimal convert(string p)
{
decimal result;
if(decimal.TryParse(p, out result))
return result;
else
return 0;
}
i have a class that starts with a variable of type TreeNode in System.Windows.Forms. The class's functions job is to add some nodes to this variable .. but the problem is when i try to add some nodes to it the debugger freeze up and doesn't show any response .. I searched over the internet but i didn't find such a problem .This is one of those functions
NOTE : the line that produce issue is commented
public Node Factor()
{
Node result = new Node();
if (count < tokens.Count && tokens[count] == TokenType.LeftParentheses)
{
this.Match(TokenType.LeftParentheses);
result = this.Expression();
if (!this.Match(TokenType.RightParentheses))
return null;
result.viewnode.Text = "Expression";
}
else if (tokens[count] == TokenType.Num)
{
if (!this.Match(TokenType.Num))
return null;
NumberNode nnode = new NumberNode(lexemes[count - 1]);
nnode.childs = "NumberNode : Value " + nnode.value + '\n';
nnode.viewnode = new TreeNode("Number - Value = " + nnode.value);
result = nnode;
result.viewnode = nnode.viewnode;
result.viewnode.Nodes.Add(nnode.viewnode);
}
else
{
if (!this.Match(TokenType.ID))
return null;
IdNode inode = new IdNode(lexemes[count - 1], "0");
inode.childs = "IdNode - Value : " + inode.name + '\n';
inode.viewnode = new TreeNode("Id - " + inode.name);
result = inode;
result.viewnode = inode.viewnode;
//the program freezes at this line
inode.viewnode.Nodes.Add(inode.viewnode);
}
return result;
}
You are adding the node to itself.
should be result.viewnode ...
I am currently trying to replicate a way of converting truth tables into Boolean expressions in C#. I have been able to generate a 3 variable (a,b,c) truth table and display it on a multiline textbox. I have created an additional eight textboxes for user to decide for each input’s output: either true(1) or false(0) . But After generating the table how can I then display the all the outputs that have a true value?
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
string newLine = Environment.NewLine;
bool a, b, c, d;
private void Form1_Load(object sender, EventArgs e)
{
textBox1.AppendText(newLine + "A" + "\t" + "B" + "\t" + "C" + newLine);
textBox1.AppendText("______________________________" + newLine);
a = true; b = true; c = true;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c +newLine);
textBox1.AppendText("______________________________" + newLine);
a = true; b = true; c = false;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = true; b = false; c = true;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = true; b = false; c = false;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = false; b = true; c = true;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = false; b = true; c = false;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = false; b = false; c = true;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
a = false; b = false; c = false;
textBox1.AppendText(newLine + a + "\t" + b + "\t" + c + newLine);
textBox1.AppendText("______________________________" + newLine);
}
private void button1_Click(object sender, EventArgs e)
{
//Grab true value outputs and display in string
}
}
Table above is an example. I would like to display true output values somehow like this:
Results Below:
FALSE TRUE TRUE
TRUE FALSE TRUE
TRUE TRUE FALSE
TRUE TRUE False
Try encapsulating your TruthItem (along with the logic to calculate the TruthValue). It would be easy to work with the truth table then (generation, iteration, calculation, etc.)
Here's sample console app. It doesn't have your textboxes, but you would get the idea.
public abstract class ThreeItemTruthRow
{
protected ThreeItemTruthRow(bool a, bool b, bool c)
{
A = a; B = b; C = c;
}
public bool A { get; protected set; }
public bool B { get; protected set; }
public bool C { get; protected set; }
public abstract bool GetTruthValue();
}
public class MyCustomThreeItemTruthRow : ThreeItemTruthRow
{
public MyCustomThreeItemTruthRow(bool a, bool b, bool c)
: base(a, b, c)
{
}
public override bool GetTruthValue()
{
// My custom logic
return (!A && B && C) || (A && !B && C) || (A && B && !C) || (A && B && C);
}
}
class Program
{
static void Main(string[] args)
{
var myTruthTable = GenerateTruthTable().ToList();
//Print only true values
foreach (var item in myTruthTable)
{
if (item.GetTruthValue())
Console.WriteLine("{0}, {1}, {2}", item.A, item.B, item.C);
}
////Print all values
//foreach (var itemTruthRow in myTruthTable)
//{
// Console.WriteLine("{0}, {1}, {2}", itemTruthRow.A, itemTruthRow.B, itemTruthRow.C);
//}
////Print only false values
//foreach (var item in myTruthTable)
//{
// if (!item.GetTruthValue())
// Console.WriteLine("{0}, {1}, {2}", item.A, item.B, item.C);
//}
Console.ReadLine();
}
public static IEnumerable<MyCustomThreeItemTruthRow> GenerateTruthTable()
{
for (var a = 0; a < 2; a++)
for (var b = 0; b < 2; b++)
for (var c = 0; c < 2; c++)
yield return new MyCustomThreeItemTruthRow(
Convert.ToBoolean(a),
Convert.ToBoolean(b),
Convert.ToBoolean(c));
}
}
EDIT (included sample code for WinForm):
Use and refer the classes above (ThreeItemTruthRow and MyCustomThreeItemTruthRow).
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void GenerateButton_Click(object sender, EventArgs e)
{
OutputTextBox.Clear();
OutputTextBox.Text += "A\tB\tC\r\n";
OutputTextBox.Text += GetHorizontalLineText();
var myTruthTable = GenerateTruthTable().ToList();
foreach(var item in myTruthTable)
{
OutputTextBox.Text += GetFormattedItemText(item);
OutputTextBox.Text += GetHorizontalLineText();
}
}
private void ShowTrueValuesButton_Click(object sender, EventArgs e)
{
OutputTextBox.Clear();
OutputTextBox.Text += "True Values\r\n";
OutputTextBox.Text += "A\tB\tC\r\n";
OutputTextBox.Text += GetHorizontalLineText();
var myTruthTable = GenerateTruthTable().ToList();
foreach(var item in myTruthTable)
{
if(item.GetTruthValue())
OutputTextBox.Text += GetFormattedItemText(item);
}
}
private static string GetHorizontalLineText()
{
return "-----------------------------------------------\r\n";
}
private static string GetFormattedItemText(MyCustomThreeItemTruthRow item)
{
return string.Format("{0}\t{1}\t{2}\r\n", item.A, item.B, item.C);
}
private static IEnumerable<MyCustomThreeItemTruthRow> GenerateTruthTable()
{
for (var a = 0; a < 2; a++)
for (var b = 0; b < 2; b++)
for (var c = 0; c < 2; c++)
yield return new MyCustomThreeItemTruthRow(
Convert.ToBoolean(a),
Convert.ToBoolean(b),
Convert.ToBoolean(c));
}
}
create a couple of classes to hold your data.
public class TruthTable {
public TruthTable(int sensorCount) {
if (sensorCount<1 || sensorCount >26) {
throw new ArgumentOutOfRangeException("sensorCount");
}
this.Table=new Sensor[(int)Math.Pow(2,sensorCount)];
for (var i=0; i < Math.Pow(2,sensorCount);i++) {
this.Table[i]=new Sensor(sensorCount);
for (var j = 0; j < sensorCount; j++) {
this.Table[i].Inputs[sensorCount - (j + 1)] = ( i / (int)Math.Pow(2, j)) % 2 == 1;
}
}
}
public Sensor[] Table {get; private set;}
public string LiveOutputs {
get {
return string.Join("\n", Table.Where(x => x.Output).Select(x => x.InputsAsString));
}
}
public string LiveOutPuts2 {
get {
return string.Join(" + ", Table.Where(x => x.Output).Select (x => x.InputsAsString2));
}
}
}
// Define other methods and classes here
public class Sensor {
public Sensor(int sensorCount) {
if (sensorCount<1 || sensorCount >26) {
throw new ArgumentOutOfRangeException("sensorCount");
}
this.SensorCount = sensorCount;
this.Inputs=new bool[sensorCount];
}
private int SensorCount {get;set;}
public bool[] Inputs { get; private set;}
public bool Output {get;set;}
public string InputsAsString {
get {
return string.Join(" ",Inputs.Select(x => x.ToString().ToUpper()));
}
}
public string InputsAsString2 {
get {
var output=new StringBuilder();
for (var i=0; i < this.SensorCount; i++) {
var letter = (char)(i+65);
output.AppendFormat("{0}{1}",Inputs[i] ? "" : "!", letter);
}
return output.ToString();
}
}
}
You can then create an instance of truth table;
var table = new TruthTable(3);
Then set the appropriate outputs to true
table.Table[3].Output=true;
table.Table[5].Output=true;
table.Table[6].Output=true;
table.Table[7].Output=true;
Then table.LiveOutputs will give you
FALSE TRUE TRUE
TRUE FALSE TRUE
TRUE TRUE FALSE
TRUE TRUE TRUE
and table.LiveOutputs2 will give you the string
!ABC + A!BC + AB!C + ABC
I've used ! to indicate false input instead of overline
EDIT --- After comment about winforms
It's been a long while since I've wirtten winforms code, I'm usually working with WPF.
Some of the code depends on how your form is generated, if you add your controls at code level...
private CheckBox[] checkBoxes;
private TruthTable table;
private int sensors;
//Call this function from the constructor....
void InitForm() {
this.sensors = 3;
this.table= new TruthTable(this.sensors);
this.checkBoxes = new CheckBox[this.sensors];
for (Var i = 0; i < this.sensors; i++) {
this.checkBox[i] = new CheckBox();
// set the positioning of the checkbox - eg this.checkBox[i].Top = 100 + (i * 30);
this.Controls.Add(this.checkBox[i]);
// You can perform similar logic to create a text label control with the sensors in it.
}
}
private void button1_Click(object sender, EventArgs e) {
for (var i=0; i<this.sensors;i++) {
this.table.Table[i].Output = this.checkBoxes[i].IsChecked;
}
this.outputTextBox.Text = this.table.LiveOutputs2;
}
Create class which will hold sensor inputs and produce output:
public class SensorInput
{
public SensorInput(bool a, bool b, bool c)
{
A = a;
B = b;
C = c;
}
public bool A { get; private set; }
public bool B { get; private set; }
public bool C { get; private set; }
public bool Output
{
// output logic goes here
get { return A || B || C; }
}
}
Then bind list of inputs to DataGridView control:
var inputs = new List<SensorInput>()
{
new SensorInput(true, true, true),
new SensorInput(true, true, false),
new SensorInput(true, false, true),
new SensorInput(true, false, false),
new SensorInput(false, true, true),
new SensorInput(false, true, false),
new SensorInput(false, false, true),
new SensorInput(false, false, false)
};
dataGridView1.DataSource = inputs;
By default boolean values will be bound to CheckBoxColumns. If you want to have True/False as text, then add four columns manually. Choose their types as (readonly) TextBoxColumns, and provide property names for binding. Result will look like:
For filtering table by output equal to true you can use Linq. Like this:
dataGridView1.DataSource = inputs.Where(i => i.Output);
I'm writing a function to take shorthand values and convert them into a standardized numeric format. Is there any standard code out there that would do "best possible" conversion of arbitrary measurement text and turn it into numeric measurements if the text is valid?
I guess I'm looking for something like bool TryParseMeasurement(string s, out decimal d). Does anyone know of a function like this?
Here's an example of some of the input values I've seen:
Imperial
6 inches
6in
6”
4 feet 2 inches
4’2”
4 ‘ 2 “
3 feet
3’
3 ‘
3ft
3ft10in
3ft 13in (should convert to 4’1”)
Metricc
1m
1.2m
1.321m
1 meter
481mm
Here's some code we wrote in an app quite some time ago, where we were doing something similar. It's not the best, but you may be able to adapt, or get some sort of jumping off point.
public static class UnitConversion
{
public static string[] lstFootUnits = new string[] {"foots", "foot", "feets", "feet", "ft", "f", "\""};
public static string sFootUnit = "ft";
public static string[] lstInchUnits = new string[] { "inches", "inchs", "inch", "in", "i", "\'" };
public static string sInchUnit = "in";
public static string[] lstPoundUnits = new string[] { "pounds", "pound", "pnds", "pnd", "lbs", "lb", "l", "p" };
public static string sPoundUnit = "lbs";
public static string[] lstOunceUnits = new string[] { "ounces", "ounce", "ozs", "oz", "o" };
public static string sOunceUnit = "oz";
public static string[] lstCentimeterUnits = new string[] { "centimeters", "centimeter", "centimetres", "centimetre", "cms", "cm", "c"};
public static string sCentimeterUnit = "cm";
public static string[] lstKilogramUnits = new string[] { "kilograms", "kilogram", "kilos", "kilo", "kgs", "kg", "k" };
public static string sKilogramsUnit = "kgs";
/// <summary>
/// Attempt to convert between feet/inches and cm
/// </summary>
/// <param name="sHeight"></param>
/// <returns></returns>
public static string ConvertHeight(string sHeight)
{
if (!String.IsNullOrEmpty(sHeight))
{
sHeight = UnitConversion.CleanHeight(sHeight);
if (sHeight.Contains(UnitConversion.sFootUnit))
{
sHeight = sHeight.Replace(UnitConversion.sFootUnit, "|");
sHeight = sHeight.Replace(UnitConversion.sInchUnit, "|");
string[] sParts = sHeight.Split('|');
double? dFeet = null;
double? dInches = null;
double dFeetParsed;
double dInchesParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dFeetParsed))
{
dFeet = dFeetParsed;
}
if (sParts.Length >= 4 && double.TryParse(sParts[2].Trim(), out dInchesParsed))
{
dInches = dInchesParsed;
};
sHeight = UnitConversion.FtToCm(UnitConversion.CalculateFt(dFeet ?? 0, dInches ?? 0)).ToString() + " " + UnitConversion.sCentimeterUnit;
}
else if (sHeight.Contains(UnitConversion.sCentimeterUnit))
{
sHeight = sHeight.Replace(UnitConversion.sCentimeterUnit, "|");
string[] sParts = sHeight.Split('|');
double? dCentimeters = null;
double dCentimetersParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dCentimetersParsed))
{
dCentimeters = dCentimetersParsed;
}
int? iFeet;
int? iInches;
if (UnitConversion.CmToFt(dCentimeters, out iFeet, out iInches))
{
sHeight = (iFeet != null) ? iFeet.ToString() + " " + UnitConversion.sFootUnit : "";
sHeight += (iInches != null) ? " " + iInches.ToString() + " " + UnitConversion.sInchUnit : "";
sHeight = sHeight.Trim();
}
else
{
sHeight = "";
}
}
else
{
sHeight = "";
}
}
else
{
sHeight = "";
}
return sHeight;
}
/// <summary>
/// Attempt to convert between Kgs and Lbs
/// </summary>
/// <param name="sWeight"></param>
/// <returns></returns>
public static string ConvertWeight(string sWeight)
{
if (!String.IsNullOrEmpty(sWeight))
{
sWeight = UnitConversion.CleanWeight(sWeight);
if (sWeight.Contains(UnitConversion.sKilogramsUnit))
{
sWeight = sWeight.Replace(UnitConversion.sKilogramsUnit, "|");
string[] sParts = sWeight.Split('|');
double? dKilograms = null;
double dKilogramsParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dKilogramsParsed))
{
dKilograms = dKilogramsParsed;
}
sWeight = UnitConversion.KgToLbs(dKilograms).ToString("#.###") + " " + UnitConversion.sPoundUnit;
}
else if (sWeight.Contains(UnitConversion.sPoundUnit))
{
sWeight = sWeight.Replace(UnitConversion.sPoundUnit, "|");
string[] sParts = sWeight.Split('|');
double? dPounds = null;
double dPoundsParsed;
if (sParts.Length >= 2 && double.TryParse(sParts[0].Trim(), out dPoundsParsed))
{
dPounds = dPoundsParsed;
}
sWeight = UnitConversion.LbsToKg(dPounds).ToString("#.###") + " " + UnitConversion.sKilogramsUnit;
}
else
{
sWeight = "";
}
}
else
{
sWeight = "";
}
return sWeight;
}
public static double? CalculateFt(double dFt, double dInch)
{
double? dFeet = null;
if (dFt >= 0 && dInch >= 0 && dInch <= 12)
{
dFeet = dFt + (dInch / 12);
}
return dFeet;
}
public static double KgToLbs(double? dKg)
{
if (dKg == null)
{
return 0;
}
return dKg.Value * 2.20462262;
}
public static double LbsToKg(double? dLbs)
{
if (dLbs == null)
{
return 0;
}
return dLbs.Value / 2.20462262;
}
public static double FtToCm(double? dFt)
{
if (dFt == null)
{
return 0;
}
return dFt.Value * 30.48;
}
public static bool CmToFt(double? dCm, out int? iFt, out int? iInch)
{
if (dCm == null)
{
iFt = null;
iInch = null;
return false;
}
double dCalcFeet = dCm.Value / 30.48;
double dCalcInches = dCalcFeet - Math.Floor(dCalcFeet);
dCalcFeet = Math.Floor(dCalcFeet);
dCalcInches = dCalcInches * 12;
iFt = (int)dCalcFeet;
iInch = (int)dCalcInches;
return true;
}
private static string CleanUnit(string sOriginal, string[] lstReplaceUnits, string sReplaceWithUnit)
{
System.Text.StringBuilder sbPattern = new System.Text.StringBuilder();
foreach (string sReplace in lstReplaceUnits)
{
if (sbPattern.Length > 0)
{
sbPattern.Append("|");
}
sbPattern.Append(sReplace);
}
sbPattern.Insert(0,#"(^|\s)(");
sbPattern.Append(#")(\s|$)");
System.Text.RegularExpressions.Regex rReplace = new System.Text.RegularExpressions.Regex(sbPattern.ToString(), System.Text.RegularExpressions.RegexOptions.IgnoreCase);
sOriginal = rReplace.Replace(sOriginal, sReplaceWithUnit);
/*foreach (string sReplace in lstReplaceUnits)
{
sOriginal = sOriginal.Replace(sReplace, " " + sReplaceWithUnit);
}*/
return sOriginal;
}
private static bool StringHasNumbers(string sText)
{
System.Text.RegularExpressions.Regex rxNumbers = new System.Text.RegularExpressions.Regex("[0-9]+");
return rxNumbers.IsMatch(sText);
}
private static string ReduceSpaces(string sText)
{
while (sText.Contains(" "))
{
sText = sText.Replace(" ", " ");
}
return sText;
}
private static string SeperateNumbers(string sText)
{
bool bNumber = false;
if (!String.IsNullOrEmpty(sText))
{
for (int iChar = 0; iChar < sText.Length; iChar++)
{
bool bIsNumber = (sText[iChar] >= '0' && sText[iChar] <= '9') ||
(sText[iChar] == '.' && iChar < sText.Length - 1 && sText[iChar + 1] >= '0' && sText[iChar + 1] <= '9');
if (iChar > 0 && bIsNumber != bNumber)
{
sText = sText.Insert(iChar, " ");
iChar++;
}
bNumber = bIsNumber;
}
}
return sText;
}
public static string CleanHeight(string sHeight)
{
if (UnitConversion.StringHasNumbers(sHeight))
{
sHeight = SeperateNumbers(sHeight);
sHeight = CleanUnit(sHeight, UnitConversion.lstFootUnits, UnitConversion.sFootUnit);
sHeight = CleanUnit(sHeight, UnitConversion.lstInchUnits, UnitConversion.sInchUnit);
sHeight = CleanUnit(sHeight, UnitConversion.lstCentimeterUnits, UnitConversion.sCentimeterUnit);
sHeight = SeperateNumbers(sHeight);
sHeight = ReduceSpaces(sHeight);
}
else
{
sHeight = "";
}
return sHeight;
}
public static string CleanWeight(string sWeight)
{
if (UnitConversion.StringHasNumbers(sWeight))
{
sWeight = SeperateNumbers(sWeight);
sWeight = CleanUnit(sWeight, UnitConversion.lstOunceUnits, UnitConversion.sOunceUnit);
sWeight = CleanUnit(sWeight, UnitConversion.lstPoundUnits, UnitConversion.sPoundUnit);
sWeight = CleanUnit(sWeight, UnitConversion.lstKilogramUnits, UnitConversion.sKilogramsUnit);
sWeight = SeperateNumbers(sWeight);
sWeight = ReduceSpaces(sWeight);
}
else
{
sWeight = "";
}
return sWeight;
}
}
It should serve you well to build an extension method of string for this purpose. When you build an extension method you attach a new function call to an existing class. In this we are go to attach a method to the 'string' class that returns a double, as the number of millimeters in a given imperial value, PROVIDED that the value can be parsed based on the examples you provide.
using System;
using System.Text;
namespace SO_Console_test
{
static class ConversionStringExtensions
{
//this is going to be a simple example you can
//fancy it up a lot...
public static double ImperialToMetric(this string val)
{
/*
* With these inputst we want to total inches.
* to do this we want to standardize the feet designator to 'f'
* and remove the inch designator altogether.
6 inches
6in
6”
4 feet 2 inches
4’2”
4 ‘ 2 “
3 feet
3’
3 ‘
3ft
3ft10in
3ft 13in (should convert to 4’1”) ...no, should convert to 49 inches, then to metric.
*/
//make the input lower case and remove blanks:
val = val.ToLower().Replace(" ", string.Empty);
//make all of the 'normal' feet designators to "ft"
string S = val.Replace("\'", "f").Replace("feet", "f").Replace("ft", "f").Replace("foot", "f").Replace("‘", "f").Replace("’", "f");
//and remove any inch designator
S = S.Replace("\"", string.Empty).Replace("inches", string.Empty).Replace("inch", string.Empty).Replace("in", string.Empty).Replace("“", string.Empty).Replace("”", string.Empty);
//finally we have to be certain we have a number of feet, even if that number is zero
S = S.IndexOf('f') > 0 ? S : "0f" + S;
//now, any of the inputs above will have been converted to a string
//that looks like 4 feet 2 inches => 4f2
string[] values = S.Split('f');
int inches = 0;
//as long as this produces one or two values we are 'on track'
if (values.Length < 3)
{
for (int i = 0; i < values.Length; i++)
{
inches += values[i] != null && values[i] != string.Empty ? int.Parse(values[i]) * (i == 0 ? 12 : 1) : 0 ;
}
}
//now inches = total number of inches in the input string.
double result = inches * 25.4;
return result;
}
}
}
With that in place "ImperialToMetric()" becomes a method of any string, and can be invoked anywhere the extension containing class ConversionStringExtensions is referenced. You can use it like:
namespace SO_Console_test
{
class Program
{
static void Main(string[] args)
{
showConversion();
Console.ReadLine();
}
private static void showConversion()
{
//simple start:
Console.WriteLine("6ft 2\"".ImperialToMetric().ToString() + " mm");
//more robust:
var Imperials = new List<string>(){"6 inches",
"6in",
"6”",
"4 feet 2 inches",
"4’2”",
"4 ‘ 2 “",
"3 feet",
"3’",
"3 ‘",
"3ft",
"3ft10in",
"3ft 13in"};
foreach (string imperial in Imperials)
{
Console.WriteLine(imperial + " converted to " + imperial.ImperialToMetric() + " millimeters");
}
}
}
Obviously, at this point a call to "Fred".ImperialToMetric is not going to play nice. You will need to had error handling and perhaps some options to turn 1234 mm 1.234 km etc. but once you flush this out you have a method you can use where ever you choose.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication5
{
class Program
{
static void Main(string[] args)
{
double km,m,f,i,cm;
Console.WriteLine("The distance between karachi and lahore in (kilometer)km is=");
km = Convert.ToInt32(Console.ReadLine());
m = km * 1000;
Console.WriteLine("The distance between karachi and lahore in meter(m) is="+m);
f = km * 3280.84;
Console.WriteLine("The distance between karachi and lahore in feet(f) is="+f);
i = km * 39370.1;
Console.WriteLine("The distance between karachi and lahore in inches(i) is="+i);
cm = m * 100;
Console.WriteLine("The distance between karachi and lahore in centimeter(cm) is="+cm);
Console.ReadLine();
}
}
}
An extension for string I wrote only to find out that there is already a solution here :) The only thing left to do is to replace "feet", "ft", "’" to "'" and "inches", "inch", "in", "“", "\"" to "''".
using System;
namespace CustomExtensions
{
public static class StringExtension
{
const float mPerFeet = 30.48f / 100;
const float mPerInch = 2.54f / 100;
// input options:
// 5'
// 5'6''
// 18''
// 24''
// 5'6
// 5 ' 6 ''
// 5' 6''
// corner cases:
// '' will return 0
// 5''6'' will interpret as 5'6''
// 5'6' will interpret as 5'6''
// 6 will interpret as 6''
// 6''' will interpret as 6''
public static float MetersFromFeetInches(this string feetInches)
{
float feet = 0;
float inches = 0;
string[] separators = new string[] { "'", "''", " " };
string[] subs = feetInches.Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (subs.Length == 1)
{
if (feetInches.Trim().EndsWith("''"))
{
float.TryParse(subs[0], out inches);
}
else if (!feetInches.Trim().EndsWith("''") && !feetInches.Trim().EndsWith("'"))
{
float.TryParse(subs[0], out inches);
}
else
{
float.TryParse(subs[0], out feet);
}
}
else if (subs.Length > 1)
{
float.TryParse(subs[0], out feet);
float.TryParse(subs[1], out inches);
}
return feet * mPerFeet + inches * mPerInch;
}
}
}