C# Class Library in WCF Service Application - c#

I have a problem with C# class library and WCF Service Application.
I built a class library with two classes Graph and Vertice. And they have few public methods inside. Then I created and published(IIS 7) a simple WCF Application, which uses this class library.
Code:
public class GraphsService : IGraphsService
{
public Graph getGraph(int val)
{
return new Graph(val);
}
}
Of course publish process went fine, and I have an access to this WCF. And now the problem:
When I created an Client Application(Web) and added service reference to this project, I can access the method getGraph(int val) but it returns class which doesn't have any of the methods I implemented in class library.
Code for client:
protected void Page_Load(object sender, EventArgs e)
{
GraphsServiceClient gc = new GraphsServiceClient();
Graph g = gc.getGraph(5);
lblGraph.Text = g.ToString();
}
That line returns me
GraphsClient.GraphService.Graph
But I want it to return my overriden method ToString(which is implemented in class library). How can I make it works?'
My Graph class from Class Library(removed few methods:
public class Graph
{
protected List<Vertice> _vertices;
public Graph()
{
this._vertices = new List<Vertice>();
}
public Graph(int startValue)
{
this._vertices = new List<Vertice>();
this._vertices.Add(new Vertice(startValue));
}
public List<Vertice> Vertices
{
get
{
return this._vertices;
}
}
public Vertice getFirstVertice()
{
if (this._vertices.Count != 0) return this._vertices.First();
throw new GraphException("Graaph doesn't have any vertices in it!");
}
public Vertice findVertice(int value)
{
foreach (Vertice v in this._vertices)
{
if (v.value == value) return v;
}
return null;
}
public Graph addVertice(Vertice v, bool undirected = true)
{
if (this._vertices.Contains(v)) throw new GraphException("There is already this vertice in graph!");
foreach (int val in v.Neighbours)
{
Vertice tmp = this.findVertice(val);
if (tmp != null)
{
if (undirected) tmp.addNeighbour(v.value);
}
else throw new GraphException("There isn't a vertice " + val + " in graph so it can't be in neighbours list!");
}
this._vertices.Add(v);
return this;
}
public int[,] getAdjacencyMatrix()
{
int[,] adMatrix = new int[this._vertices.Count + 1, this._vertices.Count + 1];
Array.Clear(adMatrix, 0, adMatrix.Length);
int l = 1;
foreach (Vertice v in this._vertices)
{
adMatrix[0, l] = v.value;
adMatrix[l, 0] = v.value;
l++;
}
for (int i = 1; i < this._vertices.Count + 1; i++)
{
for (int j = 1; j < this._vertices.Count + 1; j++)
{
int val1 = adMatrix[i, 0];
int val2 = adMatrix[0, j];
Vertice v = this.findVertice(val1);
if (v.hasNeighbour(val2)) adMatrix[i, j] = v.countNeighbours(val2);
}
}
return adMatrix;
}
public string getAdjacencyMatrixAsString()
{
string ret = "";
int[,] adM = this.getAdjacencyMatrix();
for (int i = 0; i < Math.Sqrt((double)adM.Length); i++)
{
for (int j = 0; j < Math.Sqrt((double)adM.Length); j++)
{
if (i != 0 || j != 0) ret += adM[i, j] + "\t";
else ret += "\t";
}
ret += "\n";
}
return ret;
}
public override string ToString()
{
string ret = "";
foreach (Vertice v in this._vertices)
{
ret += "Vertice: " + v.value + " neighbours: ";
foreach (int val in v.Neighbours)
{
ret += val + ", ";
}
ret = ret.Remove(ret.Length - 2);
ret += "\n";
}
ret = ret.Remove(ret.Length - 1);
return ret;
}
}

Ok, as I get it you Graph is a class marked as a DataContract and you are trying to add behavior to it that can be accessed by the client. The problem is, a DataContract is basically a DTO (Data Transfer Object). None of the behavior you define as part of a DataContract class will be made available to the consuming client as the only items serialized will be the properties. Not knowing exactly what your expectation is on the behavior of the ToString() method on the Graph object, you could add a property to the Graph Object that returns whatever piece of information you hope to get access to. That way you could do something like this.
Graph g = gc.getGraph(5);
lblGraph.Text = g.Name;
Looking at the implementation of your Graph class, I see that you are adding a whole bunch of behavior to the DataContract as suspected. I'd move all of those to the implementation of your OperationContract. None of the methods you have defined in the Graph class will be accessible to the client.

Related

C# reference array assignment issue

I'm having a little trouble reading values in from a database and assigning them to an array. It seem to work in my unit tests, but in practice some values are missing.
Here's my database code:
private void GetParameterValuesFromDatabase()
{
this.parameterValues = (from DataRow r in this.database.RunCommand("select * from KST_PARAM_VALUES v join DM_PARM_NAME p on v.PARM_NAME_KEY = p.PARM_NAME_KEY").Rows
where (int)r["SCENARIO_KEY"] == this.scenario.ScenarioKey
select new DatabaseParameter
{
ParameterValuesKey = r.Field<int>(0),
ProfileType = r.Field<string>(1),
ScenarioKey = r.Field<int>(2),
StressEditorKey = r.Field<int>(3),
StressClassKey = r.Field<int>(4),
PeriodKey = r.Field<int>(5),
ParameterNameKey = r.Field<int>(6),
ParameterValue = r.Field<double>(7),
ActiveStress = (r.Field<string>(8) == "Y") ? true : false,
ParameterKey = (int)r["PARM_NUMBER"]
}).ToDictionary(r => r.ParameterValuesKey, r => r);
}
Not having any issues with this part of my code, just showing for completeness.
private void LoadParameters()
{
this.GetParameterValuesFromDatabase();
// TODO: Assuming 9 periods for now, change to allow for variable periods
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
this.parametersByPeriod.Add(i, this.parameterValues.Where(t => t.Value.PeriodKey == i).ToDictionary(t => t.Key, t => t.Value));
}
Log.Instance.LogMessage(LogLevel.Debug, "Created parameter dictionaries from database");
// For every stress editor in the dictionary of stress editors
foreach (KeyValuePair<int, ClassList> ed in this.stressParams)
{
// For every type of class selector
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
// For each of the classes within each class list within the editor
for (int i = 0; i < ed.Value.ClassLists[c].Count; i++)
{
string className = ed.Value.ClassLists[c][i].Name;
// For each double array in each class
foreach (KeyValuePair<int, double[]> t in ed.Value.ClassLists[c][i].ClassVariables.EditorParameters)
{
double[] values = this.GetParameterValues(t.Key, ed.Key, className);
BasicStressEditorVariables.AddParameters(values, ed.Value, className, t.Key);
}
}
}
}
}
}
Above shows the overall LoadParameters() method.
Below we have some code that selects 9 values from the dictionary constructed from the database, ready to be added to the array.
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
for (int i = 1; i <= MaxNumberOfStressPeriods; i++)
{
Dictionary<int, DatabaseParameter> temp = this.parametersByPeriod[i];
foreach (KeyValuePair<int, DatabaseParameter> d in temp)
{
if (d.Value.ParameterKey == paramKey && d.Value.PeriodKey == i && d.Value.StressEditorKey == editorKey && d.Value.ProfileType == className)
{
values[i - 1] = d.Value.ParameterValue;
}
}
}
return values;
}
Below shows getting the destination array from the dictionary, as indexes cannot be passed by reference
public static void AddParameters(double[] values, ClassList editor, string className, int paramKey)
{
// TODO: Maybe search all lists to eliminate the need for the class selector as a parameter
// TODO: Will throw an exception when nothing is found. Handle it
ParameterClass p = null;
foreach (ClassSelector c in Enum.GetValues(typeof(ClassSelector)))
{
p = editor.ClassLists[c].FirstOrDefault(f => f.Name == className);
if (p != null)
{
break;
}
}
// TODO: Notify that could not be found
if (p == null)
{
Log.Instance.LogMessage(LogLevel.Error, $"Unable to find class {className}");
return;
}
double[] dest = p.ClassVariables.editorParameters[paramKey];
AddParameterValues(values, ref dest);
}
And here's the AddParameterValues() method:
private static void AddParameterValues(double[] values, ref double[] destination)
{
if (values.Length != destination.Length)
{
return;
}
for (int i = 0; i < values.Length; i++)
{
destination[i] = values[i];
}
}
Debugging shows that some values are being loaded into the destination array, but some aren't. Could anyone tell me why this is? Or if not, point me toward some material?
Thank you for your time
I'm not that C# specialist but looking to following code as a C programmer
private double[] GetParameterValues(int paramKey, int editorKey, string className)
{
double[] values = new double[9];
//...
return values;
}
I would assume that the lifetime of values is only within the function GetParameterValues and the function GetParameterValues delivers the caller with reference to a dead variable.
What if you change the prototype to something like
private void GetParameterValues(ref double[] values, int paramKey, int editorKey, string className)

How to identify unused classes in C# project

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.

Why am I getting NullReferenceException when tried to set a value to property array?

I am trying to make a Genetic Algorithm implementation for my thesis. There are two main class: Facility as chromosome and FacilityCell as gene. But I am getting an error while getting the fitness value from Facility class.
The necessary values are set in the Form.cs and after the algorithm has been run, these properties are null in the Facility instance. These properties are Facility.Flows and Facility.Demands. I can't understand why. Please help.
Code part from Form.cs
fac = new Facility();
List<FacilityCell> gens = new List<FacilityCell>();
for (int i = 0; i < 6; i++)
{
gens.Add(new FacilityCell(i.ToString(), i));
}
fac.Genes = gens.ToArray();
fac.Cells = gens.ToArray();
float[] dems = new float[3];
dems[0] = 300;
dems[1] = 60;
dems[2] = 160;
fac.Demands = dems;
FacilityCell[][] fl = new FacilityCell[3][];
fl[0] = new FacilityCell[] {
fac.Cells[0],
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[3],
fac.Cells[5] };
fl[1] = new FacilityCell[] {
fac.Cells[2],
fac.Cells[4],
fac.Cells[1],
fac.Cells[5],
fac.Cells[3],
fac.Cells[4] };
fl[2] = new FacilityCell[] {
fac.Cells[1],
fac.Cells[0],
fac.Cells[4],
fac.Cells[2],
fac.Cells[3],
fac.Cells[5] };
fac.Flows = fl;
Code from Facility.cs:
public class Facility : IChromosome
{
public Facility()
{
}
public Facility(FacilityCell[] cells)
{
this.cells = cells;
flows = null;
demands = null;
for (int i = 0; i < cells.Length; i++)
{
cells[i].Order = i;
}
}
private IGene[] cells;
private float[] demands;
private FacilityCell[][] flows;
public FacilityCell[][] Flows
{
get { return flows; }
set { flows = value; }
}
public FacilityCell[] Cells
{
get
{
return cells as FacilityCell[];
}
set
{
cells = value;
}
}
public float[] Demands
{
get { return demands; }
set { demands = value; }
}
public float FitValue
{
get
{
float total = 0;
//I AM GETTING ERROR IN THIS LINE OF CODE, THE FOR LOOP
//It throws NullReferenceException for both this.Demands and this.Flows
for (int i = 0; i < flows.Length; i++)
{
for (int j = 0; j < flows[i].Length - 1; j++)
{
int dist = Math.Abs(flows[i][j + 1].Order - flows[i][j].Order);
float totflow = dist * demands[i];
total += totflow;
}
}
return total;
}
}
public IGene[] Genes
{
get
{
return cells;
}
set
{
cells = value;
}
}
}
This code: FacilityCell[][] fl = new FacilityCell[3][]; will in the constructor set demands to null, You call ths code AFTER you set the demands.

Mixed managed C++ method does not always return the same result to the calling C# code

A C++ method returns the correct value when I use a conditional breakpoint, but an incorrect value without a breakpoint.
C# method which calls C++:
bool SetProperty(Element element, Node referencePoint, List<Materializer> materializers, List<ulong> properties)
{
// Loop over STLs
for (int i = 0; i < materializers.Count; i++)
{
Materializer materializer = materializers[i];
if (materializer.IsPointInside(referencePoint.X, referencePoint.Y, referencePoint.Z, pentalTreeDatasets[i].top))
{
element.PropertyId = properties[i];
return true;
};
}
return false;
}
C++ methods in the header file:
int CountIntersects(double x, double y, double z, PentalTreeNode ^root)
{
Math3d::M3d rayPoints[2], intersectionPoint;
rayPoints[0].set(x,y,z);
rayPoints[1].set(x,y,1.0e6);
if(!root)
return 0;
else
{
int special = CountIntersects(x,y,z,root->special);
if (x <= root->xMax && x >= root->xMin && y <= root->yMax && y >= root->yMin)
{
if( _stlMesh->IsRayIntersectsPoly(root->index, rayPoints, intersectionPoint))
{
return (1 + special);
}
else
return special;
}
else
{
if (y>root->yMax)
{
return (CountIntersects(x,y,z,root->top)+special);
}
else if(y<root->yMin)
{
return (CountIntersects(x,y,z,root->bottom)+special);
}
else if(x<root->xMin)
{
return (CountIntersects(x,y,z,root->left)+special);
}
else if(x>root->xMax)
{
return (CountIntersects(x,y,z,root->right)+special);
}
else
return special;
}
}
}
bool IsPointInside(double x, double y, double z, PentalTreeNode ^root)
{
int intersectionCount = 0;
Math3d::M3d rayPoints[2], intersectionPoint;
rayPoints[0].set(x,y,z);
rayPoints[1].set(x,y,1.0e6);
if(_box->IsContainingPoint(x,y,z))
{
intersectionCount=CountIntersects(x,y,z,root);
return (intersectionCount%2!=0);
}
}
C++ methods in other header files:
bool IsRayIntersectsPoly(int nPolygonIndex, Math3d::M3d RayPoints[2], CVector3D& IntersectionPoint)
{
CMeshPolygonBase& Poly = m_PolygonArray[nPolygonIndex];
CArrayResultI Result;
int* pPolygonPoints = GetPolygonPoints(Poly, Result);
Math3d::MPlane TrianglePlane;
double Atmp[3], A;
CVector3D* pPoints[3];
pPoints[0] = &m_PointArray[*pPolygonPoints].m_Position;
for(int i = 1; i < Result.GetSize() - 1; i++)
{
pPoints[1] = &m_PointArray[*(pPolygonPoints+i)].m_Position;
pPoints[2] = &m_PointArray[*(pPolygonPoints+i+1)].m_Position;
TrianglePlane.Init(*pPoints[0], *pPoints[1], *pPoints[2]);
TrianglePlane.IntersectLine(RayPoints[0], RayPoints[1], IntersectionPoint);
A = GetTriangleArea(*pPoints[0], *pPoints[1], *pPoints[2]);
for(int j = 0; j < 3; j++)
{
Atmp[j] = GetTriangleArea(*pPoints[j], *pPoints[(j+1)%3], IntersectionPoint);
}
if( fabs(A - Atmp[0] - Atmp[1] - Atmp[2]) < 1.0e-5 ) return true;
}
return false;
};
double GetTriangleArea(CVector3D& T1, CVector3D& T2, CVector3D& T3)
{
double a, b, c, s;
a = (T1 - T2).length();
b = (T2 - T3).length();
c = (T3 - T1).length();
s = 0.5 * (a + b + c);
return( sqrt(s * (s - a)* (s - b)* (s - c)) );
}
When I start the program which calls SetProperty() within the for-loop, the results for some iterator values are wrong. When I set conditional breakpoints for critical iterator values in the for-loop and step over it, then the result is OK for that item. What may be the problem?
This is method in which I post breakpoint. For example, for critical element.Id==2393.
private void StartButton_Click(object sender, EventArgs e)
{
DateTime startTime = DateTime.Now;
List<Materializer> materializers = new List<Materializer>();
List<ulong> properties = new List<ulong>();
// Load STLs
for (int i = 0; (int)i < (this.dataGridView.RowCount - 1); i++)
{
if (dataGridView.Rows[i].Cells[1].Value != null && (string)dataGridView.Rows[i].Cells[1].Value != "")
{
Materializer materializer = new Materializer();
materializer.LoadSTLMesh(dataGridView.Rows[i].Cells[0].Value.ToString());
materializers.Add(materializer);
properties.Add((ulong)dataGridView.Rows[i].Cells[1].Tag);
}
}
CreatePentalTrees(materializers);
int processedElementCount = 0;
int changedElementCount = 0;
// Loop over elements
foreach (Element element in model.ElementsList.Values)
if ((element.Topology == 7 || element.Topology == 8) && !lockedProperties.ContainsKey(element.PropertyId)) // 3D elements only
{
Node center = this.CenterPoint(element, model.NodesList);
if (element.Id == 2393)
{
//if breakpoints thats ok, else not ok
Console.WriteLine(element.Id);
Console.WriteLine(element.PropertyId);
}
if (SetProperty(element, center, materializers, properties)) // Check for center point
{
//changedElements.Add(element.Id, true);
changedElementCount++;
}
else
{
// Check for all nodes if center point does not belong to any STL
int[] nodeOrder;
switch (element.Topology)
{
case 7:
nodeOrder = wedgeNodeOrder;
break;
case 8:
nodeOrder = brickNodeOrder;
break;
default:
throw new Exception("Unknown topology " + element.Topology.ToString());
}
for (int i = 0; i < nodeOrder.Length; i++)
{
Node node = model.NodesList[element.NodeIds[nodeOrder[i]]];
if (SetProperty(element, node, materializers, properties))
{
//changedElements.Add(element.Id, true);
changedElementCount++;
break;
}
}
}
if (++processedElementCount % 100 == 0)
{
labelTime.Text = "Changed/processed elements: " + changedElementCount.ToString() + "/" + processedElementCount.ToString();
labelTime.Refresh();
Application.DoEvents();
}
}
DateTime endTime = DateTime.Now;
labelTime.Text = "Total time: " + (endTime - startTime).TotalSeconds.ToString() + " s";
MessageBox.Show("Completed.");
SaveFileDialog saveFileDlg = new SaveFileDialog();
saveFileDlg.Title = "Save FEMAP neutral file";
saveFileDlg.Filter = "(*.neu)|*.neu";
if (saveFileDlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
FemapNeutral.ExportNeu(saveFileDlg.FileName, model);
}
}
You seem to be calling a lot of methods you haven't listed, and/or the wall of code made me get lost. Adding that code won't help: reducing your problem to a simpler one that demonstrates the problem might.
However, the most likely cause of your problem, if you have unmanaged code reading managed data, is that you failed to marshal or pin the data prior to using the managed code.
Unpinned data can be moved around by the garbage collector in unexpected ways.

What C# template engine that has clean separation between HTML and control code?

What C# template engine
that uses 'pure' HTML having only text and markers
sans any control flow like if, while, loop or expressions,
separating html from control code ?
Below is the example phone book list code,
expressing how this should be done:
string html=#"
<html><head><title>#title</title></head>
<body>
<table>
<tr>
<td> id</td> <td> name</td> <td> sex</td> <td>phones</td>
</tr><!--#contacts:-->
<tr>
<td>#id</td> <td>#name</td> <td>#sex</td>
<td>
<!--#phones:-->#phone <br/>
<!--:#phones-->
</td>
</tr><!--:#contacts-->
</table>
</body>
</html>";
var contacts = from c in db.contacts select c;
Marker m = new Marker(html);
Filler t = m.Mark("title");
t.Set("Phone book");
Filler c = m.Mark("contacts", "id,name,sex");
// **foreach** expressed in code, not in html
foreach(var contact in contacts) {
int id = contact.id;
c.Add(id, contact.name, contact.sex);
Filler p = c.Mark("phones", "phone");
var phones = from ph in db.phones
where ph.id == id
select new {ph.phone};
if (phones.Any()) {
foreach(var ph in phones) {
p.Add(ph);
}
} else {
fp.Clear();
}
}
Console.Out.WriteLine(m.Get());
Use this code:
Templet.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace templaten.com.Templaten
{
public class tRange
{
public int head, toe;
public tRange(int _head, int _toe)
{
head = _head;
toe = _toe;
}
}
public enum AType
{
VALUE = 0,
NAME = 1,
OPEN = 2,
CLOSE = 3,
GROUP = 4
}
public class Atom
{
private AType kin;
private string tag;
private object data;
private List<Atom> bag;
public Atom(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
tag = _tag;
if (String.IsNullOrEmpty(_tag))
_kin = AType.GROUP;
kin = _kin;
if (_kin == AType.GROUP)
bag = new List<Atom>();
else
bag = null;
data = _data;
}
public AType Kin
{
get { return kin; }
}
public string Tag
{
get { return tag; }
set { tag = value; }
}
public List<Atom> Bag
{
get { return bag; }
}
public object Data
{
get { return data; }
set { data = value; }
}
public int Add(string _tag = "",
AType _kin = AType.VALUE,
object _data = null)
{
if (bag != null)
{
bag.Add(new Atom(_tag, _kin, _data));
return bag.Count - 1;
}
else
{
return -1;
}
}
}
public class Templet
{
private string content;
string namepat = "\\w+";
string justName = "(\\w+)";
string namePre = "#";
string namePost = "";
string comment0 = "\\<!--\\s*";
string comment1 = "\\s*--\\>";
private Atom tokens; // parsed contents
private Dictionary<string, int> iNames; // name index
private Dictionary<string, tRange> iGroups; // groups index
private Atom buffer; // output buffer
private Dictionary<string, int> _iname; // output name index
private Dictionary<string, tRange> _igroup; // output index
public Templet(string Content = null)
{
Init(Content);
}
private int[] mark(string[] names, string group)
{
if (names == null || names.Length < 1) return null;
tRange t = new tRange(0, buffer.Bag.Count - 1);
if (group != null)
{
if (!_igroup.ContainsKey(group)) return null;
t = _igroup[group];
}
int[] marks = new int[names.Length];
for (int i = 0; i < marks.Length; i++)
marks[i] = -1;
for (int i = t.head; i <= t.toe; i++)
{
if (buffer.Bag[i].Kin == AType.NAME)
{
for (int j = 0; j < names.Length; j++)
{
if (String.Compare(
names[j],
buffer.Bag[i].Tag,
true) == 0)
{
marks[j] = i;
break;
}
}
}
}
return marks;
}
public Filler Mark(string group, string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, group);
f.Group = group;
tRange t = null;
if (_igroup.ContainsKey(group)) t = _igroup[group];
f.Range = t;
return f;
}
public Filler Mark(string names)
{
Filler f = new Filler(this, names);
f.di = mark(f.names, null);
f.Group = "";
f.Range = null;
return f;
}
public void Set(int[] locations, object[] x)
{
int j = Math.Min(x.Length, locations.Length);
for (int i = 0; i < j; i++)
{
int l = locations[i];
if ((l >= 0) && (buffer.Bag[l] != null))
buffer.Bag[l].Data = x[i];
}
}
public void New(string group, int seq = 0)
{
// place new group copied from old group just below it
if (!( iGroups.ContainsKey(group)
&& _igroup.ContainsKey(group)
&& seq > 0)) return;
tRange newT = null;
tRange t = iGroups[group];
int beginRange = _igroup[group].toe + 1;
for (int i = t.head; i <= t.toe; i++)
{
buffer.Bag.Insert(beginRange,
new Atom(tokens.Bag[i].Tag,
tokens.Bag[i].Kin,
tokens.Bag[i].Data));
beginRange++;
}
newT = new tRange(t.toe + 1, t.toe + (t.toe - t.head + 1));
// rename past group
string pastGroup = group + "_" + seq;
t = _igroup[group];
buffer.Bag[t.head].Tag = pastGroup;
buffer.Bag[t.toe].Tag = pastGroup;
_igroup[pastGroup] = t;
// change group indexes
_igroup[group] = newT;
}
public void ReMark(Filler f, string group)
{
if (!_igroup.ContainsKey(group)) return;
Map(buffer, _iname, _igroup);
f.di = mark(f.names, group);
f.Range = _igroup[group];
}
private static void Indexing(string aname,
AType kin,
int i,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
switch (kin)
{
case AType.NAME: // index all names
dd[aname] = i;
break;
case AType.OPEN: // index all groups
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(i, -1);
else
gg[aname].head = i;
break;
case AType.CLOSE:
if (!gg.ContainsKey(aname))
gg[aname] = new tRange(-1, i);
else
gg[aname].toe = i;
break;
default:
break;
}
}
private static void Map(Atom oo,
Dictionary<string, int> dd,
Dictionary<string, tRange> gg)
{
for (int i = 0; i < oo.Bag.Count; i++)
{
string aname = oo.Bag[i].Tag;
Indexing(oo.Bag[i].Tag, oo.Bag[i].Kin, i, dd, gg);
}
}
public void Init(string Content = null)
{
content = Content;
tokens = new Atom("", AType.GROUP);
iNames = new Dictionary<string, int>();
iGroups = new Dictionary<string, tRange>();
// parse content into tokens
string namePattern = namePre + namepat + namePost;
string patterns =
"(?<var>" + namePattern + ")|" +
"(?<head>" + comment0 + namePattern + ":" + comment1 + ")|" +
"(?<toe>" + comment0 + ":" + namePattern + comment1 + ")";
Regex jn = new Regex(justName, RegexOptions.Compiled);
Regex r = new Regex(patterns, RegexOptions.Compiled);
MatchCollection ms = r.Matches(content);
int pre = 0;
foreach (Match m in ms)
{
tokens.Add(content.Substring(pre, m.Index - pre));
int idx = -1;
if (m.Groups.Count >= 3)
{
string aname = "";
MatchCollection x = jn.Matches(m.Value);
if (x.Count > 0 && x[0].Groups.Count > 1)
aname = x[0].Groups[1].ToString();
AType t = AType.VALUE;
if (m.Groups[1].Length > 0) t = AType.NAME;
if (m.Groups[2].Length > 0) t = AType.OPEN;
if (m.Groups[3].Length > 0) t = AType.CLOSE;
if (aname.Length > 0)
{
tokens.Add(aname, t);
idx = tokens.Bag.Count - 1;
}
Indexing(aname, t, idx, iNames, iGroups);
}
pre = m.Index + m.Length;
}
if (pre < content.Length)
tokens.Add(content.Substring(pre, content.Length - pre));
// copy tokens into buffer
buffer = new Atom("", AType.GROUP);
for (int i = 0; i < tokens.Bag.Count; i++)
buffer.Add(tokens.Bag[i].Tag, tokens.Bag[i].Kin);
// initialize index of output names
_iname = new Dictionary<string, int>();
foreach (string k in iNames.Keys)
_iname[k] = iNames[k];
// initialize index of output groups
_igroup = new Dictionary<string, tRange>();
foreach (string k in iGroups.Keys)
{
tRange t = iGroups[k];
_igroup[k] = new tRange(t.head, t.toe);
}
}
public string Get()
{
StringBuilder sb = new StringBuilder("");
for (int i = 0; i < buffer.Bag.Count; i++)
{
switch (buffer.Bag[i].Kin)
{
case AType.VALUE:
sb.Append(buffer.Bag[i].Tag);
break;
case AType.NAME:
sb.Append(buffer.Bag[i].Data);
break;
case AType.OPEN:
case AType.CLOSE:
break;
default: break;
}
}
return sb.ToString();
}
}
public class Filler
{
private Templet t = null;
public int[] di;
public string[] names;
public string Group { get; set; }
public tRange Range { get; set; }
private int seq = 0;
public Filler(Templet tl, string markers = null)
{
t = tl;
if (markers != null)
names = markers.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
else
names = null;
}
public void init(int length)
{
di = new int[length];
for (int i = 0; i < length; i++)
di[i] = -1;
seq = 0;
Group = "";
Range = null;
}
// clear contents inside marked object or group
public void Clear()
{
object[] x = new object[di.Length];
for (int i = 0; i < di.Length; i++)
x[i] = null;
t.Set(di, x);
}
// set value for marked object,
// or add row to group and set value to columns
public void Set(params object[] x)
{
t.Set(di, x);
}
public void Add(params object[] x)
{
if (Group.Length > 0)
{
t.New(Group, seq);
++seq;
t.ReMark(this, Group);
}
t.Set(di, x);
}
}
}
Testing program
Program.cs
Templet m = new Templet(html);
Filler f= m.Mark("title");
f.Set("Phone book");
Filler fcontacts = m.Mark("contacts", "id,name,sex,phone");
fcontacts.Add(1, "Akhmad", "M", "123456");
fcontacts.Add(2, "Barry", "M", "234567");
fcontacts.Add(1, "Charles", "M", "345678");
Console.Out.WriteLine(m.Get());
Still can't do nested loop- yet.
Just use ASP.NET. Whether you use webforms or MVC, it's super easy to have C# in your .cs files, and HTML in your .aspx files.
As with anything in programming, it's 99% up to you to do things right. Flexible UI engines aren't going to enforce that you follow good coding practices.
In principle most any template engine you choose can separate HTML from control logic with the proper architecture. using an MVC (Or MVVM) pattern, if you construct your model in such a way that the controller contains the if/then logic instead of the view you can eliminate it from the view.
That said, the syntax you use is very close to Razor syntax which is easily available for ASP.NET MVC through NuGet packages.
I totally hear you. I built SharpFusion, which has some other stuff in it but if you look for the template.cs file you will see the handler that parses a HTML file and simply replaces out tokens with values that you've made in c#.
Because no XML parsing is done like ASP.NET the framework loads much faster than even an MVC site.
Another alternative is ServiceStack.

Categories

Resources