Если Вы используете Visual C++, используете ключевое слово переопределения каждый раз, когда Вы переопределяете метод базового класса. Таким образом, если кто-либо когда-нибудь, оказывается, изменяет подпись базового класса, она бросит ошибку компилятора, а не неправильный тихо называемый метод. Это сохранило бы меня несколько раз, если бы это существовало ранее.
Пример:
class Foo
{
virtual void DoSomething();
}
class Bar: public Foo
{
void DoSomething() override { /* do something */ }
}
В конце этого ответа вы найдете полную реализацию структуры для представления адреса IPV4.
Вот действительно простой пример использования: -
List<IPV4Address> list = new List<IPV4Address>();
list.Add(IPV4Address.FromString("3.4.0.0", 24));
var x = IPV4Address.FromString("3.4.0.6");
foreach (var addr in list.Where(a => a.Contains(x)))
Console.WriteLine(addr);
Значение «3.4 .0.0 / 255.255.255.0 "отображается в консоли, поскольку 3.4.0.6 находится в подсети 3.4.0.0/24. Предполагая, что список
заполнен различными подсетями и x
может содержать любой адрес, тогда это: -
var result = list.Where(a => a.Contains(x))
.OrderByDescending(a => a.Mask)
.FirstOrDefault();
выберет наиболее конкретную подсеть, содержащую x
.
public struct IPV4Address
{
private UInt32 _Value;
private UInt32 _Mask;
public UInt32 Value
{
get { return _Value; }
private set { _Value = value; }
}
public UInt32 Mask
{
get { return _Mask; }
private set { _Mask = value; }
}
public static IPV4Address FromString(string address)
{
return FromString(address, 32);
}
public static IPV4Address FromString(string address, int maskLength)
{
string[] parts = address.Split('.');
UInt32 value = ((UInt32.Parse(parts[0]) << 24) +
((UInt32.Parse(parts[1])) << 16) +
((UInt32.Parse(parts[2])) << 8) +
UInt32.Parse(parts[3]));
return new IPV4Address(value, maskLength);
}
public IPV4Address(UInt32 value)
{
_Value = value;
_Mask = int.MaxValue;
}
public IPV4Address(UInt32 value, int maskLength)
{
if (maskLength < 0 || maskLength > 32)
throw new ArgumentOutOfRangeException("maskLength", "Must be 0 to 32");
_Value = value;
if (maskLength == 32)
_Mask = UInt32.MaxValue;
else
_Mask = ~(UInt32)((1 << (32 - maskLength))-1);
if ((_Value & _Mask) != _Value)
throw new ArgumentException("Address value must be contained in mask");
}
public bool Contains(IPV4Address address)
{
if ((Mask & address.Mask) == Mask)
{
return (address.Value & Mask) == Value;
}
return false;
}
public override string ToString()
{
string result = String.Format("{0}.{1}.{2}.{3}", (_Value >> 24),
(_Value >> 16) & 0xFF,
(_Value >> 8) & 0xFF,
_Value & 0xFF);
if (_Mask != UInt32.MaxValue)
result += "/" + String.Format("{0}.{1}.{2}.{3}", (_Mask >> 24),
(_Mask >> 16) & 0xFF,
(_Mask >> 8) & 0xFF,
_Mask & 0xFF);
return result;
}
}
Определите класс, в котором хранится IP-адрес
и длину префикса:
public class IPAddressWithPrefixLength
{
public IPAddress IPAddress { get; }
public int PrefixLength { get; }
}
Затем переопределите Equals
и GetHashCode
таким образом, чтобы принимаются во внимание только первые PrefixLength
биты IPAddress.GetAddressBytes ()
(и, конечно, тип IPAddress).
Затем вы можете использовать этот класс для хранения подсети префиксы в List
или использовать их в качестве ключей Dictionary
:
var subnets = new List<IPAddressWithPrefixLength>
{
new IPAddressWithPrefixLength(IPAddress.Parse("1.2.3.4"), 32),
new IPAddressWithPrefixLength(IPAddress.Parse("3.4.0.0"), 16),
};
var ipawpl = new IPAddressWithPrefixLength(IPAddress.Parse("3.4.5.6"), 16);
Console.WriteLine(subnets.Contains(ipawpl)); // prints "True"
Это также работает с адресами IPv6.
Я мог бы использовать двоичное дерево с булевыми метками на узлах. Используя стандартную нотацию, в которой 0 - левый дочерний элемент, а 1 - правый дочерний элемент, 1.2.3.4 будет сохранен путем помещения истины
в 00000001000000100000001100000100
(двоичное представление этого адреса) в tree - и false на всех узлах между корнем и this. И наоборот, 3.4.0.0/16 будет сохранен с истиной
в 0000001100000100
(первые 16 бит двоичного представления 3.4.0.0).
Когда вам дается адрес для проверки, просто спуститесь вниз по дереву в соответствии с битами этого адреса: если вы достигнете узла с true
, адрес будет в списке. Если вы дойдете до конца ветки, адрес отсутствует в списке.
Например, при поиске 3.4.123.48 вы: d спуститься на 16 уровней в дереве до достижения истины, что означает, что этот адрес находится в списке. Но поискав 129.199.195.13, вы узнаете по первому 1 биту в этом адресе, что он не является частью списка.
Я не уверен, насколько важно для вас использовать список
введите для хранения этих адресов, так что это может не помочь; OTOH, после того как вы реализовали базовое двоичное дерево с метками, оно должно иметь лучшие асимптотические характеристики производительности, чем .Net List
.
List
. так что это может не помочь; OTOH, как только вы реализовали базовое двоичное дерево с метками, оно должно иметь лучшие асимптотические характеристики производительности, чем .Net List
. Я бы предпочел создать специализированную структуру (класс) для хранения всей этой информации вместе. Возможно, в ближайшем будущем вы захотите расширить его для хранения ipv6 рядом с ipv4 и, возможно, еще каких-то данных (метрика, шлюз и т. Д.).
Не храните его в списке - сохраните его в такой структуре, как словарь, где ключ - это IP-адрес, а значение - адрес подсети.