C# Attributes from External Class Library - c#

I have this very basic class library that has inherited from System.Attribute. I also signed it as an assembly so the dll can be used in another program.
namespace BearData
{
public class BearData : Attribute
{
private string[] array1;
private string bear = "Bear";
private int weight;
public BearData(string bear)
{
this.bear = bear;
}
public string Bear
{
get
{
return bear;
}
set
{
bear = value;
}
}
public int Weight
{
get
{
return weight;
}
set
{
weight = value;
}
}
public string[] BearTypes()
{
array1 = new string[8];
array1[0] = "Brown/Grizzly";
array1[1] = "Polar";
array1[2] = "Asian Black";
array1[3] = "American Black";
array1[4] = "Sun";
array1[5] = "Sloth";
array1[6] = "Spectacled";
array1[7] = "Giant Panda";
return array1;
}
}
}
Here it is used in a basic console application. However due to my professor's cryptic, vague, and enigmatic nature, i'm at a stand still on getting this to work.I get an error from this line:
bearAttribute = (BearData.BearData)attrs[0];
"An unhandled exception of type 'System.IndexOutOfRangeException'
occurred in Assigntment5_Console.exe" is the exact error.
I guess my specific question what is causing that error?
But also more generally, Is this a good/proper way to use attributes when theyre from an outside library? It seems strange to me that there's random arrays thrown in here and that i'm casting an array to the attribute class?
By the way. This is how my professor wrote the code for an attribute class isolated in a single Visual Studio instance. He also had an example of class library dll exporting and I was left to my own device to figure out how to combine the 2.
using BearData;
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MemberInfo attributeInfo;
attributeInfo = typeof(BearData.BearData);
object[] attrs = attributeInfo.GetCustomAttributes(false);
//for (int i = 0; i < attrs.Length; i++)
//{
// Console.WriteLine(attrs[i]);
//}
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}

You have defined the BearData attribute on the Program.Main() method, so you should be looking for the attribute there
The following code should fix your problem
namespace Assigntment5_Console
{
class Program
{
[BearData.BearData("Bear", Weight = 1000)]
static void Main(string[] args)
{
MethodBase method = MethodBase.GetCurrentMethod();
object[] attrs = method.GetCustomAttributes(typeof(BearData.BearData), true);
BearData.BearData bearAttribute;
bearAttribute = (BearData.BearData)attrs[0];
Console.WriteLine("Animal: " + bearAttribute.Bear + "\nAverage Weight: " + bearAttribute.Weight);
Console.ReadLine();
}
}
}

Related

non-static field, error even when i declare the function as static

I am sill get a
"An object reference is required for the non-static field, method, or property..."
error even when i declare it as static.
I cant seem to hack out a fix for it. I have spent enough time on it and need help or this is my doom.
I also tied to set:
TestFeature test = new TestFeature();
and use
static public void DispData()
{
test.richTextBox1.Text = "1";
}
My goal is to dump a whole bunch of data incoming from com port into a richtextbox1.
dump[i] = string.Format("{0:X2}", hex.ToString());
DispData(ref string[] dump);
here is the full code:
namespace SW_Public
{
public partial class TestFeature : Form
{
public TestFeature()
{
InitializeComponent();
this.Text = FRM_Title.PadLeft(5);
richTextBox1.Text = "RTB1";
richTextBox2.Text = "RTB2";
}
.....
static public void DispData(ref string[] dump)
{
richTextBox1.Text = dump;
}
static void DisplayData(Byte Cmd, Byte[] RxData, int len)
{
switch (Cmd)
{
case (int)RXCMD.CMD_GETVERSION:
.....
case (int)RXCMD.CMD_RMEM:
{
string[] dump = new string[512];
for (int i = 0; i < len; i++)
{
byte hex = RxData[i];
dump[i] = string.Format("{0:X2}", hex.ToString());
DispData(ref string[] dump);
}
break;
}
}
}
}
}
Remove all of the static modifiers from all of the methods in the class.
You cannot access instance variables or methods directly from within static methods.

XML serializing Enum type properties

I'm trying to XML serialize a class containing a enum property. If the property is declared using a specific enum, it works just fine. But I need the property to be of type Enum, so I can set it to different enum types. However, when doing this I get an exception.
The type [namespace].Simple may not be used in this context.
I've tried different attributes on the enum definition, but haven't gotten it right so far. Is there a way to do this?
public enum Simple : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
public Enum Simple { get; set; }
}
public class Program
{
static void Main(string[] args)
{
using (var writer = XmlWriter.Create(Console.OpenStandardOutput()))
{
try
{
var foo = new Foo
{
Simple = Simple.three
};
var serializer = new XmlSerializer(foo.GetType());
serializer.Serialize(writer, foo);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}
You can try to set EnumMember attrubute on your DataContract that you want to serialize, for more specific info visit
https://msdn.microsoft.com/en-us/library/system.runtime.serialization.enummemberattribute(v=vs.110).aspx
Enum is abstract and cannot be serialized. A possible approach to solve is presented in this answer.
The common primitive base type of enum is int (by default, can also be byte or long for instance).
So you could as well simply use this integer base type instead, like byte Simple in your Foo class.
In case you need the string representation to appear in xml (identical to the enum field name), expose it as string Simple.
Based on dlatikay's idea about splitting the enum into two strings for enum type and member name, I've come up with the following solution. The example converts to from a Foo object to XML string, and back to a new Foo object again.
public enum SimpleEnum : byte
{
one = 0x01,
two = 0x02,
three = 0x03
}
public class Foo
{
private Enum _simple;
[XmlIgnore]
public Enum Simple
{
get { return _simple; }
set {
_simple = value;
var type = Simple.GetType();
var underlyingType = Enum.GetUnderlyingType(type);
EnumType = Simple.GetType().FullName;
EnumMember = Simple.ToString();
}
}
private string _enumType;
public string EnumType
{
get { return _enumType; }
set { _enumType = value; }
}
private string _enumMember;
public string EnumMember
{
get { return _enumMember; }
set {
_enumMember = value;
_simple = (Enum)Enum.Parse(Type.GetType(EnumType), EnumMember);
}
}
}
public class Program
{
static void Main(string[] args)
{
var str = new StringBuilder();
using (var writer = XmlWriter.Create(str))
{
try
{
var foo = new Foo
{
Simple = SimpleEnum.three
};
var serializer = new XmlSerializer(typeof(Foo));
serializer.Serialize(writer, foo);
Console.WriteLine(str.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
using (TextReader reader = new StringReader(str.ToString()))
{
try
{
var serializer = new XmlSerializer(typeof(Foo));
var foo = (Foo)serializer.Deserialize(reader);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
Console.ReadLine();
}
}

Calling a method that expects an array of objects

I'm learning C# and have written a console program to save an an array of high scores to a file. Although the program works, how I have got it to work is making me feel uneasy and feels more of a hack than a solution so I was looking for guidance on how I should have written it.
What I am currently doing within the Main method is:
Declaring an array of highscore objects
Initialising them
Assigning some values to the array.
I am happy with what I have done up until now, it's the following two steps that make me uneasy
I then declare another HighScore object
I use this object to pass the array of highscores to the SaveHighScores method.
Here is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace HighScore
{
class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
public void SaveHighScores(HighScore[] highScores)
{
string allHighScoresText = "";
foreach (HighScore score in highScores)
{
allHighScoresText += $"{score.Name},{score.Score}" + Environment.NewLine;
}
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText);
}
static void Main(string[] args)
{
HighScore[] highScore = new HighScore[2];
for (int i = 0; i < highScore.Length; i++)
{
highScore[i] = new HighScore();
}
highScore[0].Name = "A";
highScore[0].Score = 100;
highScore[1].Name = "B";
highScore[1].Score = 200;
// are the following two lines correct or am I missing something?
HighScore hs = new HighScore();
hs.SaveHighScores(highScore);
}
}
}
Make SaveHighScores static and you won't need an instance of HighScore to call it. (You can call it directly as HighScore.SaveHighScores())
I prefer to split the representation of your data from the actions that you perform on this data. So I would go for two classes, one for the Data and one for the Save/Load and other business logic
public class HighScore
{
public string Name { get; set; }
public int Score { get; set; }
}
// This class handles the core work to persist your data on the storage medium
// The class is static so you don't need to declare instances and use directly the methods available.
public static class Repo_HighScore
{
// For simplicity, no error Handling but, for a robust implementation,
// error handling is required
public static bool SaveHighScores(HighScore[] highScores)
{
StringBuilder allHighScoresText = new StringBuilder();
foreach (HighScore score in highScores)
allHighScoresText.AppendLine($"{score.Name},{score.Score}");
File.WriteAllText("C:/Temp/highscores.csv", allHighScoresText.ToString());
}
public static HighScore[] LoadHighScores()
{
List<HighScore> hs = new List<HighScore>();
foreach(string line in File.ReadLines("C:/Temp/highscores.csv"))
{
string[] parts = line.Split(',');
HighScore temp = new HighScore()
{ Name = parts[0], Score = Convert.ToInt32(parts[1])};
hs.Add(temp);
}
return hs.ToArray();
}
}

Need help correcting reflection

The following program works only with the classes which are located within the main project. Would you be so kind to advise or correct the code so that it becomes possible to use the classes from other projects. I added reference of the ClassLibrary1 project to the Example01 project.
The error which I am getting is
Unhandled Exception: System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at Example01.Program.InstantiateObject(String assemblyName, String fullName) in c:\Projects\Example01\Example01\Program.cs:line 59
object obj = Activator.CreateInstance(objectToInstantiate);
at Example01.Program.RandomizeList[TClass](Int32 count, String assemblyName) in c:\Projects\Example01\Example01\Program.cs:line 34
randomizedList.Add(
at Example01.Program.Main(String[] args) in c:\Projects\Example01\Example01\Program.cs:line 18
List<Class02> randomizedList03 = RandomizeList<Class02>();
Here is my code with the reference what works and what fails:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ClassLibrary1.Classes;
namespace Example01
{
class Program
{
static void Main(string[] args)
{
// This works
List<Class01> randomizedList01 = RandomizeList<Class01>();
// This works
List<Test01> randomizedList02 = RandomizeList<Test01>();
// This fails
List<Class02> randomizedList03 = RandomizeList<Class02>();
Console.ReadKey();
}
private static List<TClass> RandomizeList<TClass>(int count = 10, string assemblyName = "")
{
if (assemblyName.Length == 0)
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
var listOfSubClasses = SubClasses<TClass>();
var randomizedList = new List<TClass>();
var rand = new Random();
count = 10;
for (int i = 0; i < count; i++)
randomizedList.Add(
(TClass)
InstantiateObject(assemblyName,
listOfSubClasses.ElementAt(rand.Next(listOfSubClasses.Count()))
.FullName));
return new List<TClass>(randomizedList as IEnumerable<TClass>);
}
// Enumerate all subclasses for the specified class
public static IEnumerable<Type> SubClasses<TClass>()
{
var subclasses =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
from type in assembly.GetTypes()
where type.IsSubclassOf(typeof(TClass))
select type).ToList();
return subclasses;
}
private static object InstantiateObject(string assemblyName, string fullName)
{
Assembly аsm = Assembly.Load(assemblyName);
// Get the assembly metadata
Type objectToInstantiate = аsm.GetType(fullName);
// Create object on the fly
object obj = Activator.CreateInstance(objectToInstantiate);
return obj;
}
}
#region Sample Classes
public class Class01
{
public string Name { get; set; }
public int Age { get; set; }
}
public class SubClass0101 : Class01
{
public int Length { get; set; }
}
public class SubClass0102 : Class01
{
public int Length { get; set; }
}
public class SubClass0103 : Class01
{
public int Length { get; set; }
}
#endregion
}
Many thanks in advance!!!
Your problem is this line:
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name
You need to enumerate all the referenced assemblies, not just the current executing assembly, so probably need to use GetReferencedAssemblies() on the result of GetExecutingAssembly() as your main project is the executing assembly and your referenced project would be a referenced assembly.

3rd party Pdf library significantly slower when running NUnit

I am evaluating Winnovative's PdfToText library and have run into something that concerns me.
Everything runs fine and I am able to extract the text content from a small 20k or less pdf immediately if I am running a console application. However, if I call the same code from the NUnit gui running it takes 15-25 seconds (I've verified it's PdfToText by putting a breakpoint on the line that extracts the text and hitting F10 to see how long it takes to advance to the next line).
This concerns me because I'm not sure where to lay blame since I don't know the cause. Is there a problem with NUnit or PdfToText? All I want to do is extract the text from a pdf, but 20 seconds is completely unreasonable if I'm going to see this behavior under certain conditions. If it's just when running NUnit, that's acceptable, but otherwise I'll have to look elsewhere.
It's easier to demonstrate the problem using a complete VS Solution (2010), so here's the link to make it easier to setup and run (no need to download NUnit or PdfToText or even a sample pdf):
http://dl.dropbox.com/u/273037/PdfToTextProblem.zip (You may have to change the reference to PdfToText to use the x86 dll if you're running on a 32-bit machine).
Just hit F5 and the NUnit Gui runner will load.
I'm not tied to this library, if you have suggestions, I've tried iTextSharp (way too expensive for 2 lines of code), and looked at Aspose (I didn't try it, but the SaaS license is $11k). But they either lack the required functionality or are way too expensive.
(comment turned into answer)
How complex are your PDFs? The 4.1.6 version of iText allows for a closed sourced solution. Although 4.1.6 doesn't directly have a text extractor it isn't too terribly hard to write one using the PdfReader and GetPageContent().
Below is the code I used to extract the text from the PDF using iTextSharp v4.1.6. If it seems overly verbose, it's related to how I'm using it and the flexibility required.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using iTextSharp.text.pdf;
namespace ClassLibrary1
{
public class PdfToken
{
private PdfToken(int type, string value)
{
Type = type;
Value = value;
}
public static PdfToken Create(PRTokeniser tokenizer)
{
return new PdfToken(tokenizer.TokenType, tokenizer.StringValue);
}
public int Type { get; private set; }
public string Value { get; private set; }
public bool IsOperand
{
get
{
return Type == PRTokeniser.TK_OTHER;
}
}
}
public class PdfOperation
{
public PdfOperation(PdfToken operationToken, IEnumerable<PdfToken> arguments)
{
Name = operationToken.Value;
Arguments = arguments;
}
public string Name { get; private set; }
public IEnumerable<PdfToken> Arguments { get; private set; }
}
public interface IPdfParsingStrategy
{
void Execute(PdfOperation op);
}
public class PlainTextParsingStrategy : IPdfParsingStrategy
{
StringBuilder text = new StringBuilder();
public PlainTextParsingStrategy()
{
}
public String GetText()
{
return text.ToString();
}
#region IPdfParsingStrategy Members
public void Execute(PdfOperation op)
{
// see Adobe PDF specs for additional operations
switch (op.Name)
{
case "TJ":
PrintText(op);
break;
case "Tm":
SetMatrix(op);
break;
case "Tf":
SetFont(op);
break;
case "S":
PrintSection(op);
break;
case "G":
case "g":
case "rg":
SetColor(op);
break;
}
}
#endregion
bool newSection = false;
private void PrintSection(PdfOperation op)
{
text.AppendLine("------------------------------------------------------------");
newSection = true;
}
private void PrintNewline(PdfOperation op)
{
text.AppendLine();
}
private void PrintText(PdfOperation op)
{
if (newSection)
{
newSection = false;
StringBuilder header = new StringBuilder();
PrintText(op, header);
}
PrintText(op, text);
}
private static void PrintText(PdfOperation op, StringBuilder text)
{
foreach (PdfToken t in op.Arguments)
{
switch (t.Type)
{
case PRTokeniser.TK_STRING:
text.Append(t.Value);
break;
case PRTokeniser.TK_NUMBER:
text.Append(" ");
break;
}
}
}
String lastFont = String.Empty;
String lastFontSize = String.Empty;
private void SetFont(PdfOperation op)
{
var args = op.Arguments.ToList();
string font = args[0].Value;
string size = args[1].Value;
//if (font != lastFont || size != lastFontSize)
// text.AppendLine();
lastFont = font;
lastFontSize = size;
}
String lastX = String.Empty;
String lastY = String.Empty;
private void SetMatrix(PdfOperation op)
{
var args = op.Arguments.ToList();
string x = args[4].Value;
string y = args[5].Value;
if (lastY != y)
text.AppendLine();
else if (lastX != x)
text.Append(" ");
lastX = x;
lastY = y;
}
String lastColor = String.Empty;
private void SetColor(PdfOperation op)
{
lastColor = PrintCommand(op).Replace(" ", "_");
}
private static string PrintCommand(PdfOperation op)
{
StringBuilder text = new StringBuilder();
foreach (PdfToken t in op.Arguments)
text.AppendFormat("{0} ", t.Value);
text.Append(op.Name);
return text.ToString();
}
}
}
And here's how I call it:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using iTextSharp.text.pdf;
namespace ClassLibrary1
{
public class PdfExtractor
{
public static string GetText(byte[] pdfBuffer)
{
PlainTextParsingStrategy strategy = new PlainTextParsingStrategy();
ParsePdf(pdfBuffer, strategy);
return strategy.GetText();
}
private static void ParsePdf(byte[] pdf, IPdfParsingStrategy strategy)
{
PdfReader reader = new PdfReader(pdf);
for (int i = 1; i <= reader.NumberOfPages; i++)
{
byte[] page = reader.GetPageContent(i);
if (page != null)
{
PRTokeniser tokenizer = new PRTokeniser(page);
List<PdfToken> parameters = new List<PdfToken>();
while (tokenizer.NextToken())
{
var token = PdfToken.Create(tokenizer);
if (token.IsOperand)
{
strategy.Execute(new PdfOperation(token, parameters));
parameters.Clear();
}
else
{
parameters.Add(token);
}
}
}
}
}
}
}

Categories