lets assume I have the following classes:
public class ServiceStatistics
{
public string LocalId { get; set; }
public string OrganizationId { get; set; }
public List<StatisticElements> Elements { get; } = new List<StatisticElements>();
}
public class StatisticElements
{
public string StatisticId { get; set; }
public string Type { get; set; }
public string ServiceName { get; set; }
}
I retrieve such ServiceStatistics by a soap service and I use serialization/deserialization.
Each ServiceStatistics contains a set of StatisticElements. I also have a static list of StatisticElements-ID's which are relevant for calculation. All other incoming StatisticElements-ID's can be dropped. I need to do this on my side
because the SOAP Service does not support selecting specific StatisticElements-ID's
So I have generated a static Class with a HashSet:
public static class RelevantDutyPlans
{
private static HashSet<int> relevantDutyPlans;
static RelevantDutyPlans()
{
// only a subset of the original ID's
relevantDutyPlans = new HashSet<int>()
{
530,
1150,
1095,
};
}
public static HashSet<int> GetRelevantDutyPlans()
{
return relevantDutyPlans;
}
public static bool Contains(int planId)
{
return relevantDutyPlans.Contains(planId);
}
// Extracts all DutyPlans which are relevant (HashSet) for validation from
// the incoming data
public static List<int> ExtractRelevantDutyPlans(List<int> planIds)
{
var relevantPlans = new HashSet<int>(planIds);
relevantPlans.IntersectWith(relevantDutyPlans);
return relevantDutyPlans.ToList();
}
}
So my thought was, to create an Intersect like this:
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
List<int> incomingIds = new List<int>();
foreach(var item in statistics)
{
foreach(var element in item.Statistic)
{
incomingIds.Add(int.Parse(element.StatisticId));
}
}
List<int> extract = RelevantDutyPlans.ExtractRelevantDutyPlans(incomingIds);
So now I have a List of ID's which are relevant for further processing. What I want to achieve is to remove all class elements "StatisticElements" with "StatisticId" not contained in the the extract list generated above.
Any ideas?
Any help is very appreciated
How about a little bit different approach. Simply remove irrelevant plans right away!
List<ServiceStatistics> statistics = SoapService.GetStatistics(Now);
foreach(var item in statistics)
{
item.Elements.RemoveAll(x => !RelevantDutyPlans.Contains(int.Parse(x.StatisticId)));
}
Now you are only left with the relevant once.
Hope you can use selectMany to flatten the collection and proceed the filter.
var filteredItems = statistics.SelectMany(s => s.Elements)
.Where(s => extract.Contains(Convert.ToInt32(s.StatisticId)))
.ToList();
You could also use LINQ to create a new List<> if you need to keep the original statistcs intact - e.g. if you might run multiple plans against it.
var relevantStatistics = statistics.Select(s => new {
LocalId = s.LocalId,
OrganizationId = s.OrganizationId,
Elements = s.Elements.Where(e => !RelevantDutyPlans.Contains(Convert.ToInt32(e.StatisticId))).ToList()
});
Since ServiceStatistics doesn't provide for construction, I return an anonymous object instead, but you could create an appropriate DTO class.
I am trying to make a function that works with a list of an interface but I would like to keep the concrete class values for those lists. For example:
public interface GUID_OBJECT
{
string GUID { get; set; }
}
public class Car : GUID_OBJECT
{
public string GUID { get; set; }
public string CarSpecifikProp { get; set; }
}
public class Bike : GUID_OBJECT
{
public string GUID { get; set; }
public string BikeSpecifikProp { get; set; }
}
class Program
{
static void Main(string[] args)
{
var cars = new List<Car>() { new Car() { GUID = "1", CarSpecifikProp = "carstuff" }, new Car() { GUID = "2", CarSpecifikProp = "carsuff" } };
var bikes = new List<Bike>() { new Bike() { GUID = "1", BikeSpecifikProp = "bikestuff" }, new Bike() { GUID = "2", BikeSpecifikProp = "bikestuff" } };
var filteredCars = FilterGuidObjects(cars);
foreach (var car in filteredCars)
{
car.CarSpecifikProp = "changethis";
}
var filteredBikes = FilterGuidObjects(bikes);
foreach (var bike in filteredBikes)
{
bike.BikeSpecifikProp = "changethis";
}
Console.ReadKey();
}
static List<GUID_OBJECT> FilterGuidObjects(List<GUID_OBJECT> objects)
{
return objects.Where(x => x.GUID == "1").ToList();
}
}
This code doesn't work but it illustrates what I would like to do. I have a web api project that returns various type of lists of objects, but all these objects have some things in common such as GUID for example. Can I write functions that work with the interface GUID_OBJECT but keep the Car and Bike specific properties after that function returns its List of GUID_OBJECT interfaces? How would you deal with a scenario like this?
make a generic method with a constraint:
static List<G> FilterGuidObjects<G>(List<G> objects) where G: GUID_OBJECT
{
return objects.Where(x => x.GUID == "1").ToList();
}
The point of interfaces is to have only the common properties of the classes that implement them. If you need to use a specific property you'll need to cast to the type you want:
var obj = (MyType) myInterface
Since you're working with lists, you can cast the entire list like this:
listOfInterface.Cast<MyType>().ToList()
Consider implementing FilterGuidObjects as a generic function:
static List<T> FilterGuidObjects<T>(List<T> objects) where T : GUID_OBJECT
{
return objects.Where(x => x.GUID == "1").ToList();
}
there are many ways of achieving this but the crux of the matter is you have to convert the interface back to the original type
Method 1: generic filter on the function
static List<T> FilterGuidObjects(List<T> objects)
:where T:GUID_OBJECT
{
return objects.Where(x => x.GUID == "1").ToList();
}
Pros Quick and simple, will even infer the type automatically
Cons you can't feed in a Mixed list
Method 2 Type Filter the collection
var filteredCars = FilterGuidObjects(MixedListOfVechiles).OfType<Car>();
Pros Can handle mixed lists
Con have to specify the conversion every time
Method 3 Convert the Type Directly
var car = guid_object as Car
if(car != null)
car.CarSpecifikProp = "changethis";
or
if(guid_object is Car)
{
var car = (Car)guid_object
car.CarSpecifikProp = "changethis";
}
Pros doesn't use generics
Con requires lots of code
Xunit has a nice feature: you can create one test with a Theory attribute and put data in InlineData attributes, and xUnit will generate many tests, and test them all.
I want to have something like this, but the parameters to my method are not 'simple data' (like string, int, double), but a list of my class:
public static void WriteReportsToMemoryStream(
IEnumerable<MyCustomClass> listReport,
MemoryStream ms,
StreamWriter writer) { ... }
There are many xxxxData attributes in XUnit. Check out for example the MemberData attribute.
You can implement a property that returns IEnumerable<object[]>. Each object[] that this method generates will be then "unpacked" as a parameters for a single call to your [Theory] method.
See i.e. these examples from here
Here are some examples, just for a quick glance.
MemberData Example: just here at hand
public class StringTests2
{
[Theory, MemberData(nameof(SplitCountData))]
public void SplitCount(string input, int expectedCount)
{
var actualCount = input.Split(' ').Count();
Assert.Equal(expectedCount, actualCount);
}
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "xUnit", 1 },
new object[] { "is fun", 2 },
new object[] { "to test with", 3 }
};
}
XUnit < 2.0: Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
ClassData Example
public class StringTests3
{
[Theory, ClassData(typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
private readonly List<object[]> _data = new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
public IEnumerator<object[]> GetEnumerator()
{ return _data.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator()
{ return GetEnumerator(); }
}
XUnit >= 2.0: Instead of ClassData, now there's an 'overload' of [MemberData] that allows to use static members from other classes. Examples below have been updated to use it, since XUnit < 2.x is pretty ancient now.
Another option is ClassData, which works the same, but allows to easily share the 'generators' between tests in different classes/namespaces, and also separates the 'data generators' from the actual test methods.
MemberData Example: look there to another type
public class StringTests3
{
[Theory, MemberData(nameof(IndexOfData.SplitCountData), MemberType = typeof(IndexOfData))]
public void IndexOf(string input, char letter, int expected)
{
var actual = input.IndexOf(letter);
Assert.Equal(expected, actual);
}
}
public class IndexOfData : IEnumerable<object[]>
{
public static IEnumerable<object[]> SplitCountData =>
new List<object[]>
{
new object[] { "hello world", 'w', 6 },
new object[] { "goodnight moon", 'w', -1 }
};
}
Disclaimer :)
Last time checked #20210903 with dotnetfiddle.net on C# 5.0 and xunit 2.4.1 .. and failed. I couldn't mix-in a test-runner into that fiddle. But at least it compiled fine. Note that this was originally written years ago, things changed a little. I fixed them according to my hunch and comments. So.. it may contain inobvious typos, otherwise obvious bugs that would instantly pop up at runtime, and traces of milk & nuts.
Suppose that we have a complex Car class that has a Manufacturer class:
public class Car
{
public int Id { get; set; }
public long Price { get; set; }
public Manufacturer Manufacturer { get; set; }
}
public class Manufacturer
{
public string Name { get; set; }
public string Country { get; set; }
}
We're going to fill and pass the Car class to a Theory test.
So create a 'CarClassData' class that returns an instance of the Car class like below:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="country",
Name="name"
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
It's time for creating a test method(CarTest) and define the car as a parameter:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(Car car)
{
var output = car;
var result = _myRepository.BuyCar(car);
}
**If you're going to pass a list of car objects to Theory then change the CarClassData as follow:
public class CarClassData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] {
new List<Car>()
{
new Car
{
Id=1,
Price=36000000,
Manufacturer = new Manufacturer
{
Country="Iran",
Name="arya"
}
},
new Car
{
Id=2,
Price=45000,
Manufacturer = new Manufacturer
{
Country="Torbat",
Name="kurosh"
}
}
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
And the theory will be:
[Theory]
[ClassData(typeof(CarClassData))]
public void CarTest(List<Car> cars)
{
var output = cars;
}
Good Luck
To update #Quetzalcoatl's answer: The attribute [PropertyData] has been superseded by [MemberData] which takes as argument the string name of any static method, field, or property that returns an IEnumerable<object[]>. (I find it particularly nice to have an iterator method that can actually calculate test cases one at a time, yielding them up as they're computed.)
Each element in the sequence returned by the enumerator is an object[] and each array must be the same length and that length must be the number of arguments to your test case (annotated with the attribute [MemberData] and each element must have the same type as the corresponding method parameter. (Or maybe they can be convertible types, I don't know.)
(See release notes for xUnit.net March 2014 and the actual patch with example code.)
Creating anonymous object arrays is not the easiest way to construct the data so I used this pattern in my project.
First define some reusable, shared classes:
//http://stackoverflow.com/questions/22093843
public interface ITheoryDatum
{
object[] ToParameterArray();
}
public abstract class TheoryDatum : ITheoryDatum
{
public abstract object[] ToParameterArray();
public static ITheoryDatum Factory<TSystemUnderTest, TExpectedOutput>(TSystemUnderTest sut, TExpectedOutput expectedOutput, string description)
{
var datum= new TheoryDatum<TSystemUnderTest, TExpectedOutput>();
datum.SystemUnderTest = sut;
datum.Description = description;
datum.ExpectedOutput = expectedOutput;
return datum;
}
}
public class TheoryDatum<TSystemUnderTest, TExpectedOutput> : TheoryDatum
{
public TSystemUnderTest SystemUnderTest { get; set; }
public string Description { get; set; }
public TExpectedOutput ExpectedOutput { get; set; }
public override object[] ToParameterArray()
{
var output = new object[3];
output[0] = SystemUnderTest;
output[1] = ExpectedOutput;
output[2] = Description;
return output;
}
}
Now your individual test and member data is easier to write and cleaner...
public class IngredientTests : TestBase
{
[Theory]
[MemberData(nameof(IsValidData))]
public void IsValid(Ingredient ingredient, bool expectedResult, string testDescription)
{
Assert.True(ingredient.IsValid == expectedResult, testDescription);
}
public static IEnumerable<object[]> IsValidData
{
get
{
var food = new Food();
var quantity = new Quantity();
var data= new List<ITheoryDatum>();
data.Add(TheoryDatum.Factory(new Ingredient { Food = food } , false, "Quantity missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity } , false, "Food missing"));
data.Add(TheoryDatum.Factory(new Ingredient { Quantity = quantity, Food = food } , true, "Valid" ));
return data.ConvertAll(d => d.ToParameterArray());
}
}
}
The string Description property is to throw yourself a bone when one of your many test cases fail.
You can try this way:
public class TestClass {
bool isSaturday(DateTime dt)
{
string day = dt.DayOfWeek.ToString();
return (day == "Saturday");
}
[Theory]
[MemberData("IsSaturdayIndex", MemberType = typeof(TestCase))]
public void test(int i)
{
// parse test case
var input = TestCase.IsSaturdayTestCase[i];
DateTime dt = (DateTime)input[0];
bool expected = (bool)input[1];
// test
bool result = isSaturday(dt);
result.Should().Be(expected);
}
}
Create another class to hold the test data:
public class TestCase
{
public static readonly List<object[]> IsSaturdayTestCase = new List<object[]>
{
new object[]{new DateTime(2016,1,23),true},
new object[]{new DateTime(2016,1,24),false}
};
public static IEnumerable<object[]> IsSaturdayIndex
{
get
{
List<object[]> tmp = new List<object[]>();
for (int i = 0; i < IsSaturdayTestCase.Count; i++)
tmp.Add(new object[] { i });
return tmp;
}
}
}
For my needs I just wanted to run a series of 'test users' through some tests - but [ClassData] etc. seemed overkill for what I needed (because the list of items was localized to each test).
So I did the following, with an array inside the test - indexed from the outside:
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public async Task Account_ExistingUser_CorrectPassword(int userIndex)
{
// DIFFERENT INPUT DATA (static fake users on class)
var user = new[]
{
EXISTING_USER_NO_MAPPING,
EXISTING_USER_MAPPING_TO_DIFFERENT_EXISTING_USER,
EXISTING_USER_MAPPING_TO_SAME_USER,
NEW_USER
} [userIndex];
var response = await Analyze(new CreateOrLoginMsgIn
{
Username = user.Username,
Password = user.Password
});
// expected result (using ExpectedObjects)
new CreateOrLoginResult
{
AccessGrantedTo = user.Username
}.ToExpectedObject().ShouldEqual(response);
}
This achieved my goal, while keeping the intent of the test clear. You just need to keep the indexes in sync but that's all.
Looks nice in the results, it's collapsable and you can rerun a specific instance if you get an error:
This is how I solved your problem, I had the same scenario. So inline with custom objects and a different number of objects on each run.
[Theory]
[ClassData(typeof(DeviceTelemetryTestData))]
public async Task ProcessDeviceTelemetries_TypicalDeserialization_NoErrorAsync(params DeviceTelemetry[] expected)
{
// Arrange
var timeStamp = DateTimeOffset.UtcNow;
mockInflux.Setup(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>())).ReturnsAsync("Success");
// Act
var actual = await MessageProcessingTelemetry.ProcessTelemetry(JsonConvert.SerializeObject(expected), mockInflux.Object);
// Assert
mockInflux.Verify(x => x.ExportTelemetryToDb(It.IsAny<List<DeviceTelemetry>>()), Times.Once);
Assert.Equal("Success", actual);
}
So this is my unit test, notice the params parameter. This allow to send a different number of object. And now my DeviceTelemetryTestData class :
public class DeviceTelemetryTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
yield return new object[] { new DeviceTelemetry { DeviceId = "asd" }, new DeviceTelemetry { DeviceId = "qwe" } };
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
Hope it helps !
Despite this has already been answered I just want to add an improvement here.
The restriction of passing objects in InlineData attribute is not a limitiation of xUnit itself but C# attributes.
See this compiler error: Compiler Error CS0182
xUnit.Sdk provides you with DataAttribute class that you could inherit and override its GetData method and use it to pass whatever you feel like you want..
I usually use it alongside DataTestBuilders pattern and build something like that..
public class ValidComplexObjectDataSource : DataAttribute
{
public override IEnumerable<object[]> GetData(MethodInfo testMethod)
{
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.Build()
};
yield return new object[] {
ComplexObjectBuilder
.BasicComplexObject()
.WithoutSomeAttribute()
.Build()
};
// ... list all test cases you want to pass to your method
}
}
This ComplexObjectBuilder could be whatever your object is, highly recommend checking builder pattern
[Theory]
[Trait("Validation", "CreateXYZCommand")]
[ValidComplexObjectDataSource]
public void CreateXYZCommandValidator_WithValidInput_ShouldPassAllValidations(CreateComplexObjectInput createComplexObjectInput)
{
var command = new CreateXYZCommand(createComplexObjectInput);
var result = _validator.TestValidate(command);
result.ShouldNotHaveAnyValidationErrors();
}
I only demonstrated it with a single object, you have an array of objects you can yield.
yield return new object[] {
ComplexObject_1,
ComplexObject_2,
string_attribute,
int_attribute
};
and have these as arguments to your test cases.
You can utilize TheoryData for complex types like classes.
[Theory, MemberData(nameof(CustomClassTests))]
public async Task myTestName(MyCustomClass customClassTestData) { ... }
public record MyCustomClass { ... }
public static TheoryData<MyCustomClass> CustomClassTests {
get {
return new() {
new MyCustomClass{ ... },
new MyCustomClass{ ... },
...
};
}
}
I guess you mistaken here. What xUnit Theory attribute actually means: You want to test this function by sending special/random values as parameters that this function-under-test receives. That means that what you define as the next attribute, such as: InlineData, PropertyData, ClassData, etc.. will be the source for those parameters. That means that you should construct the source object to provide those parameters. In your case I guess you should use ClassData object as source. Also - please note that ClassData inherits from: IEnumerable<> - that means each time another set of generated parameters will be used as incoming parameters for function-under-test until IEnumerable<> produces values.
Example here: Tom DuPont .NET
Example may be incorrect - I didn't use xUnit for a long time
I am accessing a REST API which returns a list of resources in JSON format:
{
"products": [
{ ... },
{ ... }
]
}
When the list is big (>50 items) the response becomes paginated and an additional pagination item is added to the root node of returned JSON like that:
{
"pagination": {
"results" : 490,
"page" : 1,
"page_size" : 50,
"pages" : 10
},
"products": [
{ ... },
{ ... }
]
}
In order to cater for that I have a PaginatedList class (probably not the best name) which looks like that:
public class PaginatedList
{
[JsonProperty("pagination")]
public Pagination Pagination { get; set; }
}
a ProductList class that looks like that:
public class ProductList : PaginatedList
{
[JsonProperty("products")]
public List<Product> Products { get; set; }
}
a Pagination class like that:
public class Pagination
{
[JsonProperty("results")]
public int Results { get; set; }
[JsonProperty("page")]
public int Page { get; set; }
[JsonProperty("page_size")]
public int PageSize { get; set; }
[JsonProperty("pages")]
public int Pages { get; set; }
}
To retrieve my resource I use:
public List<Product> GetProducts()
{
return getResourceAsync<ProductList>(productsResourceName).Result.Products;
}
and:
async Task<T> getResourceListAsync<T>(string resourceName)
{
var url = string.Concat(BaseUrl, resourceName);
var credentials = new NetworkCredential(Username, Password);
var handler = new HttpClientHandler { Credentials = credentials };
using (var client = new HttpClient(handler)) {
var response = await client.GetAsync(url);
var contentString = await response.Content.ReadAsStringAsync();
var resource = await JsonConvert.DeserializeObjectAsync<T>(contentString);
return resource;
}
}
Adding support for pagination inside the GetProducts method would be pretty easy but that would mean duplicating very similar code for every type of resource (products, customers, suppliers, etc). The question is, how do I get the getResourceListAsync method so that it supports paginated and non-paginated lists AND works for different resources?
To make this scenario possible :
// supports both scenarios
var json = #"{ 'products': [ { Id : 1 , Name : 'A' }, { Id : 2 , Name : 'B' } ] }";
var results = Helper.ParseFromJsonResult<Product>(json);
var anotherJson = #"{ 'pagination': {
'results' : 490,
'page' : 1,
'page_size' : 50,
'pages' : 10
},
'products': [
{ Id : 1 , Name : 'A' }, { Id : 2 , Name : 'B' }
]}";
var anotherResults = Helper.ParseFromJsonResult<Product>(anotherJson);
You can use this codes :
public static class Helper
{
private static readonly PluralizationService _NameService =
PluralizationService.CreateService(new CultureInfo("en-us"));
// Provides Plural names [ products for Product ]
// to determinate the name of the result for example products for Product class
private static ModuleBuilder _ModuleBuilder;
static Helper()
{
var asmName = new AssemblyName();
asmName.Name = "MyHelpers";
AssemblyBuilder asmBuilder = Thread.GetDomain().DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run);
_ModuleBuilder = asmBuilder.DefineDynamicModule("MyHelpers");
// Assembly to put runtime generated classes to that.
}
private static readonly IDictionary<Type, Type> _HelpersCache = new Dictionary<Type, Type>();
public static List<T> ParseFromJsonResult<T>(String json)
{
Type resultType = null;
var entityType = typeof(T);
var pluralName = _NameService.Pluralize(entityType.Name).ToLowerInvariant();
// products for Product class
if (_HelpersCache.ContainsKey(entityType))
{
// better performance
resultType = _HelpersCache[entityType];
}
else
{
// need another runtime generated class
// result :
/* public class products
{
public List<Product> products;
}
*/
TypeBuilder resultTypeBuilder = _ModuleBuilder.DefineType(pluralName, TypeAttributes.Public);
FieldBuilder field = resultTypeBuilder.DefineField(pluralName, typeof(List<T>), FieldAttributes.Public);
resultType = resultTypeBuilder.CreateType();
_HelpersCache.Add(entityType, resultType);
}
Object result = JsonConvert.DeserializeObject(json, resultType);
return (List<T>)resultType.GetField(pluralName).GetValue(result); // get products field value
}
}
I hope this helps, Let me know if you want more information
Good luck
I see your problem. Was stuck in the same one actually. What I ended up doing was an interface that my classes i want to paginte implement. As I did this for export of data it is called IExportable. This thing returns a PagedResult and takes a IPagedRequest.
So the point is that my code in the task is just working with interface and does not actually know what the end type is. PagedResult is a type with generics.
Hope it helps, trying to write this from top of my head without the source.
How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.